Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/sel1.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*====================================================================* | |
| 2 - Copyright (C) 2001 Leptonica. All rights reserved. | |
| 3 - | |
| 4 - Redistribution and use in source and binary forms, with or without | |
| 5 - modification, are permitted provided that the following conditions | |
| 6 - are met: | |
| 7 - 1. Redistributions of source code must retain the above copyright | |
| 8 - notice, this list of conditions and the following disclaimer. | |
| 9 - 2. Redistributions in binary form must reproduce the above | |
| 10 - copyright notice, this list of conditions and the following | |
| 11 - disclaimer in the documentation and/or other materials | |
| 12 - provided with the distribution. | |
| 13 - | |
| 14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY | |
| 18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
| 23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| 24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 *====================================================================*/ | |
| 26 | |
| 27 | |
| 28 /*! | |
| 29 * \file sel1.c | |
| 30 * <pre> | |
| 31 * | |
| 32 * Basic ops on Sels and Selas | |
| 33 * | |
| 34 * Create/destroy/copy: | |
| 35 * SELA *selaCreate() | |
| 36 * void selaDestroy() | |
| 37 * SEL *selCreate() | |
| 38 * void selDestroy() | |
| 39 * SEL *selCopy() | |
| 40 * SEL *selCreateBrick() | |
| 41 * SEL *selCreateComb() | |
| 42 * | |
| 43 * Helper proc: | |
| 44 * l_int32 **create2dIntArray() | |
| 45 * | |
| 46 * Extension of sela: | |
| 47 * SELA *selaAddSel() | |
| 48 * static l_int32 selaExtendArray() | |
| 49 * | |
| 50 * Accessors: | |
| 51 * l_int32 selaGetCount() | |
| 52 * SEL *selaGetSel() | |
| 53 * char *selGetName() | |
| 54 * l_int32 selSetName() | |
| 55 * l_int32 selaFindSelByName() | |
| 56 * l_int32 selGetElement() | |
| 57 * l_int32 selSetElement() | |
| 58 * l_int32 selGetParameters() | |
| 59 * l_int32 selSetOrigin() | |
| 60 * l_int32 selGetTypeAtOrigin() | |
| 61 * char *selaGetBrickName() | |
| 62 * char *selaGetCombName() | |
| 63 * static char *selaComputeCompositeParameters() | |
| 64 * l_int32 getCompositeParameters() | |
| 65 * SARRAY *selaGetSelnames() | |
| 66 * | |
| 67 * Max translations for erosion and hmt | |
| 68 * l_int32 selFindMaxTranslations() | |
| 69 * | |
| 70 * Rotation by multiples of 90 degrees | |
| 71 * SEL *selRotateOrth() | |
| 72 * | |
| 73 * Sela and Sel serialized I/O | |
| 74 * SELA *selaRead() | |
| 75 * SELA *selaReadStream() | |
| 76 * SEL *selRead() | |
| 77 * SEL *selReadStream() | |
| 78 * l_int32 selaWrite() | |
| 79 * l_int32 selaWriteStream() | |
| 80 * l_int32 selWrite() | |
| 81 * l_int32 selWriteStream() | |
| 82 * | |
| 83 * Building custom hit-miss sels from compiled strings | |
| 84 * SEL *selCreateFromString() | |
| 85 * char *selPrintToString() [for debugging] | |
| 86 * | |
| 87 * Building custom hit-miss sels from a simple file format | |
| 88 * SELA *selaCreateFromFile() | |
| 89 * static SEL *selCreateFromSArray() | |
| 90 * | |
| 91 * Making hit-only sels from Pta and Pix | |
| 92 * SEL *selCreateFromPta() | |
| 93 * SEL *selCreateFromPix() | |
| 94 * | |
| 95 * Making hit-miss sels from Pix and image files | |
| 96 * SEL *selReadFromColorImage() | |
| 97 * SEL *selCreateFromColorPix() | |
| 98 SELA *selaCreateFromColorPixa() | |
| 99 * | |
| 100 * Printable display of sel | |
| 101 * PIX *selDisplayInPix() | |
| 102 * PIX *selaDisplayInPix() | |
| 103 * | |
| 104 * Usage notes: | |
| 105 * In this file we have seven functions that make sels: | |
| 106 * (1) selCreate(), with input (h, w, [name]) | |
| 107 * The generic function. Roll your own, using selSetElement(). | |
| 108 * (2) selCreateBrick(), with input (h, w, cy, cx, val) | |
| 109 * The most popular function. Makes a rectangular sel of | |
| 110 * all hits, misses or don't-cares. We have many morphology | |
| 111 * operations that create a sel of all hits, use it, and | |
| 112 * destroy it. | |
| 113 * (3) selCreateFromString() with input (text, h, w, [name]) | |
| 114 * Adam Langley's clever function, allows you to make a hit-miss | |
| 115 * sel from a string in code that is geometrically laid out | |
| 116 * just like the actual sel. | |
| 117 * (4) selaCreateFromFile() with input (filename) | |
| 118 * This parses a simple file format to create an array of | |
| 119 * hit-miss sels. The sel data uses the same encoding | |
| 120 * as in (3), with geometrical layout enforced. | |
| 121 * (5) selCreateFromPta() with input (pta, cy, cx, [name]) | |
| 122 * Another way to make a sel with only hits. | |
| 123 * (6) selCreateFromPix() with input (pix, cy, cx, [name]) | |
| 124 * Yet another way to make a sel from hits. | |
| 125 * (7) selCreateFromColorPix() with input (pix, name). | |
| 126 * Another way to make a general hit-miss sel, starting with | |
| 127 * an image editor. | |
| 128 * In addition, there are three functions in selgen.c that | |
| 129 * automatically generate a hit-miss sel from a pix and | |
| 130 * a number of parameters. This is useful for problems like | |
| 131 * "find all patterns that look like this one." | |
| 132 * | |
| 133 * Consistency, being the hobgoblin of small minds, | |
| 134 * is adhered to here in the dimensioning and accessing of sels. | |
| 135 * Everything is done in standard matrix (row, column) order. | |
| 136 * When we set specific elements in a sel, we likewise use | |
| 137 * (row, col) ordering: | |
| 138 * selSetElement(), with input (row, col, type) | |
| 139 * </pre> | |
| 140 */ | |
| 141 | |
| 142 #ifdef HAVE_CONFIG_H | |
| 143 #include <config_auto.h> | |
| 144 #endif /* HAVE_CONFIG_H */ | |
| 145 | |
| 146 #include <string.h> | |
| 147 #include "allheaders.h" | |
| 148 | |
| 149 /* Bounds on sel ptr array size */ | |
| 150 static const l_uint32 MaxPtrArraySize = 10000; | |
| 151 static const l_int32 InitialPtrArraySize = 50; /*!< n'importe quoi */ | |
| 152 | |
| 153 /* Bounds on kernel size */ | |
| 154 static const l_uint32 MaxKernelSize = 10000; | |
| 155 | |
| 156 /* Bounds on pix template size */ | |
| 157 static const l_uint32 MaxPixTemplateSize = 300; | |
| 158 static const l_uint32 MaxPixTemplateHits = 3000; | |
| 159 | |
| 160 /* Static functions */ | |
| 161 static l_int32 selaExtendArray(SELA *sela); | |
| 162 static SEL *selCreateFromSArray(SARRAY *sa, l_int32 first, l_int32 last); | |
| 163 | |
| 164 struct CompParameterMap | |
| 165 { | |
| 166 l_int32 size; | |
| 167 l_int32 size1; | |
| 168 l_int32 size2; | |
| 169 char selnameh1[20]; | |
| 170 char selnameh2[20]; | |
| 171 char selnamev1[20]; | |
| 172 char selnamev2[20]; | |
| 173 }; | |
| 174 | |
| 175 static const struct CompParameterMap comp_parameter_map[] = | |
| 176 { { 2, 2, 1, "sel_2h", "", "sel_2v", "" }, | |
| 177 { 3, 3, 1, "sel_3h", "", "sel_3v", "" }, | |
| 178 { 4, 2, 2, "sel_2h", "sel_comb_4h", "sel_2v", "sel_comb_4v" }, | |
| 179 { 5, 5, 1, "sel_5h", "", "sel_5v", "" }, | |
| 180 { 6, 3, 2, "sel_3h", "sel_comb_6h", "sel_3v", "sel_comb_6v" }, | |
| 181 { 7, 7, 1, "sel_7h", "", "sel_7v", "" }, | |
| 182 { 8, 4, 2, "sel_4h", "sel_comb_8h", "sel_4v", "sel_comb_8v" }, | |
| 183 { 9, 3, 3, "sel_3h", "sel_comb_9h", "sel_3v", "sel_comb_9v" }, | |
| 184 { 10, 5, 2, "sel_5h", "sel_comb_10h", "sel_5v", "sel_comb_10v" }, | |
| 185 { 11, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, | |
| 186 { 12, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, | |
| 187 { 13, 4, 3, "sel_4h", "sel_comb_12h", "sel_4v", "sel_comb_12v" }, | |
| 188 { 14, 7, 2, "sel_7h", "sel_comb_14h", "sel_7v", "sel_comb_14v" }, | |
| 189 { 15, 5, 3, "sel_5h", "sel_comb_15h", "sel_5v", "sel_comb_15v" }, | |
| 190 { 16, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, | |
| 191 { 17, 4, 4, "sel_4h", "sel_comb_16h", "sel_4v", "sel_comb_16v" }, | |
| 192 { 18, 6, 3, "sel_6h", "sel_comb_18h", "sel_6v", "sel_comb_18v" }, | |
| 193 { 19, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, | |
| 194 { 20, 5, 4, "sel_5h", "sel_comb_20h", "sel_5v", "sel_comb_20v" }, | |
| 195 { 21, 7, 3, "sel_7h", "sel_comb_21h", "sel_7v", "sel_comb_21v" }, | |
| 196 { 22, 11, 2, "sel_11h", "sel_comb_22h", "sel_11v", "sel_comb_22v" }, | |
| 197 { 23, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, | |
| 198 { 24, 6, 4, "sel_6h", "sel_comb_24h", "sel_6v", "sel_comb_24v" }, | |
| 199 { 25, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, | |
| 200 { 26, 5, 5, "sel_5h", "sel_comb_25h", "sel_5v", "sel_comb_25v" }, | |
| 201 { 27, 9, 3, "sel_9h", "sel_comb_27h", "sel_9v", "sel_comb_27v" }, | |
| 202 { 28, 7, 4, "sel_7h", "sel_comb_28h", "sel_7v", "sel_comb_28v" }, | |
| 203 { 29, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, | |
| 204 { 30, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, | |
| 205 { 31, 6, 5, "sel_6h", "sel_comb_30h", "sel_6v", "sel_comb_30v" }, | |
| 206 { 32, 8, 4, "sel_8h", "sel_comb_32h", "sel_8v", "sel_comb_32v" }, | |
| 207 { 33, 11, 3, "sel_11h", "sel_comb_33h", "sel_11v", "sel_comb_33v" }, | |
| 208 { 34, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, | |
| 209 { 35, 7, 5, "sel_7h", "sel_comb_35h", "sel_7v", "sel_comb_35v" }, | |
| 210 { 36, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, | |
| 211 { 37, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, | |
| 212 { 38, 6, 6, "sel_6h", "sel_comb_36h", "sel_6v", "sel_comb_36v" }, | |
| 213 { 39, 13, 3, "sel_13h", "sel_comb_39h", "sel_13v", "sel_comb_39v" }, | |
| 214 { 40, 8, 5, "sel_8h", "sel_comb_40h", "sel_8v", "sel_comb_40v" }, | |
| 215 { 41, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, | |
| 216 { 42, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, | |
| 217 { 43, 7, 6, "sel_7h", "sel_comb_42h", "sel_7v", "sel_comb_42v" }, | |
| 218 { 44, 11, 4, "sel_11h", "sel_comb_44h", "sel_11v", "sel_comb_44v" }, | |
| 219 { 45, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, | |
| 220 { 46, 9, 5, "sel_9h", "sel_comb_45h", "sel_9v", "sel_comb_45v" }, | |
| 221 { 47, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, | |
| 222 { 48, 8, 6, "sel_8h", "sel_comb_48h", "sel_8v", "sel_comb_48v" }, | |
| 223 { 49, 7, 7, "sel_7h", "sel_comb_49h", "sel_7v", "sel_comb_49v" }, | |
| 224 { 50, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, | |
| 225 { 51, 10, 5, "sel_10h", "sel_comb_50h", "sel_10v", "sel_comb_50v" }, | |
| 226 { 52, 13, 4, "sel_13h", "sel_comb_52h", "sel_13v", "sel_comb_52v" }, | |
| 227 { 53, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, | |
| 228 { 54, 9, 6, "sel_9h", "sel_comb_54h", "sel_9v", "sel_comb_54v" }, | |
| 229 { 55, 11, 5, "sel_11h", "sel_comb_55h", "sel_11v", "sel_comb_55v" }, | |
| 230 { 56, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, | |
| 231 { 57, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, | |
| 232 { 58, 8, 7, "sel_8h", "sel_comb_56h", "sel_8v", "sel_comb_56v" }, | |
| 233 { 59, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, | |
| 234 { 60, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, | |
| 235 { 61, 10, 6, "sel_10h", "sel_comb_60h", "sel_10v", "sel_comb_60v" }, | |
| 236 { 62, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" }, | |
| 237 { 63, 9, 7, "sel_9h", "sel_comb_63h", "sel_9v", "sel_comb_63v" } }; | |
| 238 | |
| 239 | |
| 240 | |
| 241 /*------------------------------------------------------------------------* | |
| 242 * Create / Destroy / Copy * | |
| 243 *------------------------------------------------------------------------*/ | |
| 244 /*! | |
| 245 * \brief selaCreate() | |
| 246 * | |
| 247 * \param[in] n initial number of sel ptrs; use 0 for default | |
| 248 * \return sela, or NULL on error | |
| 249 */ | |
| 250 SELA * | |
| 251 selaCreate(l_int32 n) | |
| 252 { | |
| 253 SELA *sela; | |
| 254 | |
| 255 if (n <= 0 || n > (l_int32)MaxPtrArraySize) | |
| 256 n = InitialPtrArraySize; | |
| 257 | |
| 258 /* Make array of sel ptrs */ | |
| 259 sela = (SELA *)LEPT_CALLOC(1, sizeof(SELA)); | |
| 260 sela->nalloc = n; | |
| 261 sela->n = 0; | |
| 262 sela->sel = (SEL **)LEPT_CALLOC(n, sizeof(SEL *)); | |
| 263 return sela; | |
| 264 } | |
| 265 | |
| 266 | |
| 267 /*! | |
| 268 * \brief selaDestroy() | |
| 269 * | |
| 270 * \param[in,out] psela will be set to null before returning | |
| 271 * \return void | |
| 272 */ | |
| 273 void | |
| 274 selaDestroy(SELA **psela) | |
| 275 { | |
| 276 SELA *sela; | |
| 277 l_int32 i; | |
| 278 | |
| 279 if (!psela) return; | |
| 280 if ((sela = *psela) == NULL) | |
| 281 return; | |
| 282 | |
| 283 for (i = 0; i < sela->n; i++) | |
| 284 selDestroy(&sela->sel[i]); | |
| 285 LEPT_FREE(sela->sel); | |
| 286 LEPT_FREE(sela); | |
| 287 *psela = NULL; | |
| 288 } | |
| 289 | |
| 290 | |
| 291 /*! | |
| 292 * \brief selCreate() | |
| 293 * | |
| 294 * \param[in] height | |
| 295 * \param[in] width | |
| 296 * \param[in] name [optional] sel name; can be null | |
| 297 * \return sel, or NULL on error | |
| 298 * | |
| 299 * <pre> | |
| 300 * Notes: | |
| 301 * (1) selCreate() initializes all values to 0. | |
| 302 * (2) After this call, (cy,cx) and nonzero data values must be | |
| 303 * assigned. If a text name is not assigned here, it will | |
| 304 * be needed later when the sel is put into a sela. | |
| 305 * </pre> | |
| 306 */ | |
| 307 SEL * | |
| 308 selCreate(l_int32 height, | |
| 309 l_int32 width, | |
| 310 const char *name) | |
| 311 { | |
| 312 SEL *sel; | |
| 313 | |
| 314 sel = (SEL *)LEPT_CALLOC(1, sizeof(SEL)); | |
| 315 if (name) | |
| 316 sel->name = stringNew(name); | |
| 317 sel->sy = height; | |
| 318 sel->sx = width; | |
| 319 if ((sel->data = create2dIntArray(height, width)) == NULL) { | |
| 320 LEPT_FREE(sel->name); | |
| 321 LEPT_FREE(sel); | |
| 322 return (SEL *)ERROR_PTR("data not allocated", __func__, NULL); | |
| 323 } | |
| 324 | |
| 325 return sel; | |
| 326 } | |
| 327 | |
| 328 | |
| 329 /*! | |
| 330 * \brief selDestroy() | |
| 331 * | |
| 332 * \param[in,out] psel will be set to null before returning | |
| 333 * \return void | |
| 334 */ | |
| 335 void | |
| 336 selDestroy(SEL **psel) | |
| 337 { | |
| 338 l_int32 i; | |
| 339 SEL *sel; | |
| 340 | |
| 341 if (psel == NULL) { | |
| 342 L_WARNING("ptr address is NULL!\n", __func__); | |
| 343 return; | |
| 344 } | |
| 345 if ((sel = *psel) == NULL) | |
| 346 return; | |
| 347 | |
| 348 for (i = 0; i < sel->sy; i++) | |
| 349 LEPT_FREE(sel->data[i]); | |
| 350 LEPT_FREE(sel->data); | |
| 351 if (sel->name) | |
| 352 LEPT_FREE(sel->name); | |
| 353 LEPT_FREE(sel); | |
| 354 *psel = NULL; | |
| 355 } | |
| 356 | |
| 357 | |
| 358 /*! | |
| 359 * \brief selCopy() | |
| 360 * | |
| 361 * \param[in] sel | |
| 362 * \return a copy of the sel, or NULL on error | |
| 363 */ | |
| 364 SEL * | |
| 365 selCopy(SEL *sel) | |
| 366 { | |
| 367 l_int32 sx, sy, cx, cy, i, j; | |
| 368 SEL *csel; | |
| 369 | |
| 370 if (!sel) | |
| 371 return (SEL *)ERROR_PTR("sel not defined", __func__, NULL); | |
| 372 | |
| 373 csel = (SEL *)LEPT_CALLOC(1, sizeof(SEL)); | |
| 374 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 375 csel->sy = sy; | |
| 376 csel->sx = sx; | |
| 377 csel->cy = cy; | |
| 378 csel->cx = cx; | |
| 379 | |
| 380 if ((csel->data = create2dIntArray(sy, sx)) == NULL) { | |
| 381 LEPT_FREE(csel); | |
| 382 return (SEL *)ERROR_PTR("sel data not made", __func__, NULL); | |
| 383 } | |
| 384 | |
| 385 for (i = 0; i < sy; i++) | |
| 386 for (j = 0; j < sx; j++) | |
| 387 csel->data[i][j] = sel->data[i][j]; | |
| 388 | |
| 389 if (sel->name) | |
| 390 csel->name = stringNew(sel->name); | |
| 391 | |
| 392 return csel; | |
| 393 } | |
| 394 | |
| 395 | |
| 396 /*! | |
| 397 * \brief selCreateBrick() | |
| 398 * | |
| 399 * \param[in] h, w height, width | |
| 400 * \param[in] cy, cx origin, relative to UL corner at 0,0 | |
| 401 * \param[in] type SEL_HIT, SEL_MISS, or SEL_DONT_CARE | |
| 402 * \return sel, or NULL on error | |
| 403 * | |
| 404 * <pre> | |
| 405 * Notes: | |
| 406 * (1) This is a rectangular sel of all hits, misses or don't cares. | |
| 407 * </pre> | |
| 408 */ | |
| 409 SEL * | |
| 410 selCreateBrick(l_int32 h, | |
| 411 l_int32 w, | |
| 412 l_int32 cy, | |
| 413 l_int32 cx, | |
| 414 l_int32 type) | |
| 415 { | |
| 416 l_int32 i, j; | |
| 417 SEL *sel; | |
| 418 | |
| 419 if (h <= 0 || w <= 0) | |
| 420 return (SEL *)ERROR_PTR("h and w must both be > 0", __func__, NULL); | |
| 421 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) | |
| 422 return (SEL *)ERROR_PTR("invalid sel element type", __func__, NULL); | |
| 423 | |
| 424 if ((sel = selCreate(h, w, NULL)) == NULL) | |
| 425 return (SEL *)ERROR_PTR("sel not made", __func__, NULL); | |
| 426 selSetOrigin(sel, cy, cx); | |
| 427 for (i = 0; i < h; i++) | |
| 428 for (j = 0; j < w; j++) | |
| 429 sel->data[i][j] = type; | |
| 430 | |
| 431 return sel; | |
| 432 } | |
| 433 | |
| 434 | |
| 435 /*! | |
| 436 * \brief selCreateComb() | |
| 437 * | |
| 438 * \param[in] factor1 contiguous space between comb tines | |
| 439 * \param[in] factor2 number of comb tines | |
| 440 * \param[in] direction L_HORIZ, L_VERT | |
| 441 * \return sel, or NULL on error | |
| 442 * | |
| 443 * <pre> | |
| 444 * Notes: | |
| 445 * (1) This generates a comb Sel of hits with the origin as | |
| 446 * near the center as possible. | |
| 447 * (2) In use, this is complemented by a brick sel of size %factor1, | |
| 448 * Both brick and comb sels are made by selectComposableSels(). | |
| 449 * </pre> | |
| 450 */ | |
| 451 SEL * | |
| 452 selCreateComb(l_int32 factor1, | |
| 453 l_int32 factor2, | |
| 454 l_int32 direction) | |
| 455 { | |
| 456 l_int32 i, size, z; | |
| 457 SEL *sel; | |
| 458 | |
| 459 if (factor1 < 1 || factor2 < 1) | |
| 460 return (SEL *)ERROR_PTR("factors must be >= 1", __func__, NULL); | |
| 461 if (direction != L_HORIZ && direction != L_VERT) | |
| 462 return (SEL *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 463 | |
| 464 size = factor1 * factor2; | |
| 465 if (direction == L_HORIZ) { | |
| 466 if ((sel = selCreate(1, size, NULL)) == NULL) | |
| 467 return (SEL *)ERROR_PTR("horiz sel not made", __func__, NULL); | |
| 468 selSetOrigin(sel, 0, size / 2); | |
| 469 } else { | |
| 470 if ((sel = selCreate(size, 1, NULL)) == NULL) | |
| 471 return (SEL *)ERROR_PTR("vert sel not made", __func__, NULL); | |
| 472 selSetOrigin(sel, size / 2, 0); | |
| 473 } | |
| 474 | |
| 475 /* Lay down the elements of the comb */ | |
| 476 for (i = 0; i < factor2; i++) { | |
| 477 z = factor1 / 2 + i * factor1; | |
| 478 /* lept_stderr("i = %d, factor1 = %d, factor2 = %d, z = %d\n", | |
| 479 i, factor1, factor2, z); */ | |
| 480 if (direction == L_HORIZ) | |
| 481 selSetElement(sel, 0, z, SEL_HIT); | |
| 482 else | |
| 483 selSetElement(sel, z, 0, SEL_HIT); | |
| 484 } | |
| 485 | |
| 486 return sel; | |
| 487 } | |
| 488 | |
| 489 | |
| 490 /*! | |
| 491 * \brief create2dIntArray() | |
| 492 * | |
| 493 * \param[in] sy rows == height | |
| 494 * \param[in] sx columns == width | |
| 495 * \return doubly indexed array i.e., an array of sy row pointers, | |
| 496 * each of which points to an array of sx ints | |
| 497 * | |
| 498 * <pre> | |
| 499 * Notes: | |
| 500 * (1) The array[sy][sx] is indexed in standard "matrix notation", | |
| 501 * with the row index first. | |
| 502 * </pre> | |
| 503 */ | |
| 504 l_int32 ** | |
| 505 create2dIntArray(l_int32 sy, | |
| 506 l_int32 sx) | |
| 507 { | |
| 508 l_int32 i; | |
| 509 l_int32 **array; | |
| 510 | |
| 511 if (sx <= 0 || sx > (l_int32)MaxKernelSize) | |
| 512 return (l_int32 **)ERROR_PTR("sx out of bounds", __func__, NULL); | |
| 513 if (sy <= 0 || sy > (l_int32)MaxKernelSize) | |
| 514 return (l_int32 **)ERROR_PTR("sy out of bounds", __func__, NULL); | |
| 515 | |
| 516 array = (l_int32 **)LEPT_CALLOC(sy, sizeof(l_int32 *)); | |
| 517 for (i = 0; i < sy; i++) | |
| 518 array[i] = (l_int32 *)LEPT_CALLOC(sx, sizeof(l_int32)); | |
| 519 return array; | |
| 520 } | |
| 521 | |
| 522 | |
| 523 /*------------------------------------------------------------------------* | |
| 524 * Extension of sela * | |
| 525 *------------------------------------------------------------------------*/ | |
| 526 /*! | |
| 527 * \brief selaAddSel() | |
| 528 * | |
| 529 * \param[in] sela | |
| 530 * \param[in] sel to be added | |
| 531 * \param[in] selname ignored if already defined in sel; | |
| 532 * req'd in sel when added to a sela | |
| 533 * \param[in] copyflag L_INSERT or L_COPY | |
| 534 * \return 0 if OK; 1 on error | |
| 535 * | |
| 536 * <pre> | |
| 537 * Notes: | |
| 538 * (1) This adds a sel, either inserting or making a copy. | |
| 539 * (2) Because every sel in a sela must have a name, it copies | |
| 540 * the input name if necessary. You can input NULL for | |
| 541 * selname if the sel already has a name. | |
| 542 * </pre> | |
| 543 */ | |
| 544 l_ok | |
| 545 selaAddSel(SELA *sela, | |
| 546 SEL *sel, | |
| 547 const char *selname, | |
| 548 l_int32 copyflag) | |
| 549 { | |
| 550 l_int32 n; | |
| 551 SEL *csel; | |
| 552 | |
| 553 if (!sela) | |
| 554 return ERROR_INT("sela not defined", __func__, 1); | |
| 555 if (!sel) | |
| 556 return ERROR_INT("sel not defined", __func__, 1); | |
| 557 if (!sel->name && !selname) | |
| 558 return ERROR_INT("added sel must have name", __func__, 1); | |
| 559 if (copyflag != L_INSERT && copyflag != L_COPY) | |
| 560 return ERROR_INT("invalid copyflag", __func__, 1); | |
| 561 | |
| 562 if (copyflag == L_COPY) { | |
| 563 if ((csel = selCopy(sel)) == NULL) | |
| 564 return ERROR_INT("csel not made", __func__, 1); | |
| 565 } else { /* copyflag == L_INSERT */ | |
| 566 csel = sel; | |
| 567 } | |
| 568 if (!csel->name) | |
| 569 csel->name = stringNew(selname); | |
| 570 | |
| 571 n = selaGetCount(sela); | |
| 572 if (n >= sela->nalloc) { | |
| 573 if (selaExtendArray(sela)) { | |
| 574 if (copyflag != L_INSERT) | |
| 575 selDestroy(&csel); | |
| 576 return ERROR_INT("extension failed", __func__, 1); | |
| 577 } | |
| 578 } | |
| 579 | |
| 580 sela->sel[n] = csel; | |
| 581 sela->n++; | |
| 582 return 0; | |
| 583 } | |
| 584 | |
| 585 | |
| 586 /*! | |
| 587 * \brief selaExtendArray() | |
| 588 * | |
| 589 * \param[in] sela | |
| 590 * \return 0 if OK; 1 on error | |
| 591 */ | |
| 592 static l_int32 | |
| 593 selaExtendArray(SELA *sela) | |
| 594 { | |
| 595 if (!sela) | |
| 596 return ERROR_INT("sela not defined", __func__, 1); | |
| 597 | |
| 598 if ((sela->sel = (SEL **)reallocNew((void **)&sela->sel, | |
| 599 sizeof(SEL *) * sela->nalloc, | |
| 600 2 * sizeof(SEL *) * sela->nalloc)) == NULL) | |
| 601 return ERROR_INT("new ptr array not returned", __func__, 1); | |
| 602 | |
| 603 sela->nalloc = 2 * sela->nalloc; | |
| 604 return 0; | |
| 605 } | |
| 606 | |
| 607 | |
| 608 | |
| 609 /*----------------------------------------------------------------------* | |
| 610 * Accessors * | |
| 611 *----------------------------------------------------------------------*/ | |
| 612 /*! | |
| 613 * \brief selaGetCount() | |
| 614 * | |
| 615 * \param[in] sela | |
| 616 * \return count, or 0 on error | |
| 617 */ | |
| 618 l_int32 | |
| 619 selaGetCount(SELA *sela) | |
| 620 { | |
| 621 if (!sela) | |
| 622 return ERROR_INT("sela not defined", __func__, 0); | |
| 623 | |
| 624 return sela->n; | |
| 625 } | |
| 626 | |
| 627 | |
| 628 /*! | |
| 629 * \brief selaGetSel() | |
| 630 * | |
| 631 * \param[in] sela | |
| 632 * \param[in] i index of sel to be retrieved not copied | |
| 633 * \return sel, or NULL on error | |
| 634 * | |
| 635 * <pre> | |
| 636 * Notes: | |
| 637 * (1) This returns a ptr to the sel, not a copy, so the caller | |
| 638 * must not destroy it! | |
| 639 * </pre> | |
| 640 */ | |
| 641 SEL * | |
| 642 selaGetSel(SELA *sela, | |
| 643 l_int32 i) | |
| 644 { | |
| 645 if (!sela) | |
| 646 return (SEL *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 647 | |
| 648 if (i < 0 || i >= sela->n) | |
| 649 return (SEL *)ERROR_PTR("invalid index", __func__, NULL); | |
| 650 return sela->sel[i]; | |
| 651 } | |
| 652 | |
| 653 | |
| 654 /*! | |
| 655 * \brief selGetName() | |
| 656 * | |
| 657 * \param[in] sel | |
| 658 * \return sel name not copied, or NULL if no name or on error | |
| 659 */ | |
| 660 char * | |
| 661 selGetName(SEL *sel) | |
| 662 { | |
| 663 if (!sel) | |
| 664 return (char *)ERROR_PTR("sel not defined", __func__, NULL); | |
| 665 | |
| 666 return sel->name; | |
| 667 } | |
| 668 | |
| 669 | |
| 670 /*! | |
| 671 * \brief selSetName() | |
| 672 * | |
| 673 * \param[in] sel | |
| 674 * \param[in] name [optional]; can be null | |
| 675 * \return 0 if OK, 1 on error | |
| 676 * | |
| 677 * <pre> | |
| 678 * Notes: | |
| 679 * (1) Always frees the existing sel name, if defined. | |
| 680 * (2) If name is not defined, just clears any existing sel name. | |
| 681 * </pre> | |
| 682 */ | |
| 683 l_ok | |
| 684 selSetName(SEL *sel, | |
| 685 const char *name) | |
| 686 { | |
| 687 if (!sel) | |
| 688 return ERROR_INT("sel not defined", __func__, 1); | |
| 689 | |
| 690 return stringReplace(&sel->name, name); | |
| 691 } | |
| 692 | |
| 693 | |
| 694 /*! | |
| 695 * \brief selaFindSelByName() | |
| 696 * | |
| 697 * \param[in] sela | |
| 698 * \param[in] name sel name | |
| 699 * \param[out] pindex [optional] | |
| 700 * \param[in] psel [optional] sel (not a copy) | |
| 701 * \return 0 if OK; 1 on error | |
| 702 */ | |
| 703 l_ok | |
| 704 selaFindSelByName(SELA *sela, | |
| 705 const char *name, | |
| 706 l_int32 *pindex, | |
| 707 SEL **psel) | |
| 708 { | |
| 709 l_int32 i, n; | |
| 710 char *sname; | |
| 711 SEL *sel; | |
| 712 | |
| 713 if (pindex) *pindex = -1; | |
| 714 if (psel) *psel = NULL; | |
| 715 | |
| 716 if (!sela) | |
| 717 return ERROR_INT("sela not defined", __func__, 1); | |
| 718 | |
| 719 n = selaGetCount(sela); | |
| 720 for (i = 0; i < n; i++) | |
| 721 { | |
| 722 if ((sel = selaGetSel(sela, i)) == NULL) { | |
| 723 L_WARNING("missing sel\n", __func__); | |
| 724 continue; | |
| 725 } | |
| 726 | |
| 727 sname = selGetName(sel); | |
| 728 if (sname && (!strcmp(name, sname))) { | |
| 729 if (pindex) | |
| 730 *pindex = i; | |
| 731 if (psel) | |
| 732 *psel = sel; | |
| 733 return 0; | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 return 1; | |
| 738 } | |
| 739 | |
| 740 | |
| 741 /*! | |
| 742 * \brief selGetElement() | |
| 743 * | |
| 744 * \param[in] sel | |
| 745 * \param[in] row | |
| 746 * \param[in] col | |
| 747 * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE | |
| 748 * \return 0 if OK; 1 on error | |
| 749 */ | |
| 750 l_ok | |
| 751 selGetElement(SEL *sel, | |
| 752 l_int32 row, | |
| 753 l_int32 col, | |
| 754 l_int32 *ptype) | |
| 755 { | |
| 756 if (!ptype) | |
| 757 return ERROR_INT("&type not defined", __func__, 1); | |
| 758 *ptype = SEL_DONT_CARE; | |
| 759 if (!sel) | |
| 760 return ERROR_INT("sel not defined", __func__, 1); | |
| 761 if (row < 0 || row >= sel->sy) | |
| 762 return ERROR_INT("sel row out of bounds", __func__, 1); | |
| 763 if (col < 0 || col >= sel->sx) | |
| 764 return ERROR_INT("sel col out of bounds", __func__, 1); | |
| 765 | |
| 766 *ptype = sel->data[row][col]; | |
| 767 return 0; | |
| 768 } | |
| 769 | |
| 770 | |
| 771 /*! | |
| 772 * \brief selSetElement() | |
| 773 * | |
| 774 * \param[in] sel | |
| 775 * \param[in] row | |
| 776 * \param[in] col | |
| 777 * \param[in] type SEL_HIT, SEL_MISS, SEL_DONT_CARE | |
| 778 * \return 0 if OK; 1 on error | |
| 779 * | |
| 780 * <pre> | |
| 781 * Notes: | |
| 782 * (1) Because we use row and column to index into an array, | |
| 783 * they are always non-negative. The location of the origin | |
| 784 * (and the type of operation) determine the actual | |
| 785 * direction of the rasterop. | |
| 786 * </pre> | |
| 787 */ | |
| 788 l_ok | |
| 789 selSetElement(SEL *sel, | |
| 790 l_int32 row, | |
| 791 l_int32 col, | |
| 792 l_int32 type) | |
| 793 { | |
| 794 if (!sel) | |
| 795 return ERROR_INT("sel not defined", __func__, 1); | |
| 796 if (type != SEL_HIT && type != SEL_MISS && type != SEL_DONT_CARE) | |
| 797 return ERROR_INT("invalid sel element type", __func__, 1); | |
| 798 if (row < 0 || row >= sel->sy) | |
| 799 return ERROR_INT("sel row out of bounds", __func__, 1); | |
| 800 if (col < 0 || col >= sel->sx) | |
| 801 return ERROR_INT("sel col out of bounds", __func__, 1); | |
| 802 | |
| 803 sel->data[row][col] = type; | |
| 804 return 0; | |
| 805 } | |
| 806 | |
| 807 | |
| 808 /*! | |
| 809 * \brief selGetParameters() | |
| 810 * | |
| 811 * \param[in] sel | |
| 812 * \param[out] psy, psx, pcy, pcx [optional] each can be null | |
| 813 * \return 0 if OK, 1 on error | |
| 814 */ | |
| 815 l_ok | |
| 816 selGetParameters(SEL *sel, | |
| 817 l_int32 *psy, | |
| 818 l_int32 *psx, | |
| 819 l_int32 *pcy, | |
| 820 l_int32 *pcx) | |
| 821 { | |
| 822 if (psy) *psy = 0; | |
| 823 if (psx) *psx = 0; | |
| 824 if (pcy) *pcy = 0; | |
| 825 if (pcx) *pcx = 0; | |
| 826 if (!sel) | |
| 827 return ERROR_INT("sel not defined", __func__, 1); | |
| 828 if (psy) *psy = sel->sy; | |
| 829 if (psx) *psx = sel->sx; | |
| 830 if (pcy) *pcy = sel->cy; | |
| 831 if (pcx) *pcx = sel->cx; | |
| 832 return 0; | |
| 833 } | |
| 834 | |
| 835 | |
| 836 /*! | |
| 837 * \brief selSetOrigin() | |
| 838 * | |
| 839 * \param[in] sel | |
| 840 * \param[in] cy, cx | |
| 841 * \return 0 if OK; 1 on error | |
| 842 */ | |
| 843 l_ok | |
| 844 selSetOrigin(SEL *sel, | |
| 845 l_int32 cy, | |
| 846 l_int32 cx) | |
| 847 { | |
| 848 if (!sel) | |
| 849 return ERROR_INT("sel not defined", __func__, 1); | |
| 850 sel->cy = cy; | |
| 851 sel->cx = cx; | |
| 852 return 0; | |
| 853 } | |
| 854 | |
| 855 | |
| 856 /*! | |
| 857 * \brief selGetTypeAtOrigin() | |
| 858 * | |
| 859 * \param[in] sel | |
| 860 * \param[out] ptype SEL_HIT, SEL_MISS, SEL_DONT_CARE | |
| 861 * \return 0 if OK; 1 on error or if origin is not found | |
| 862 */ | |
| 863 l_ok | |
| 864 selGetTypeAtOrigin(SEL *sel, | |
| 865 l_int32 *ptype) | |
| 866 { | |
| 867 l_int32 sx, sy, cx, cy, i, j; | |
| 868 | |
| 869 if (!ptype) | |
| 870 return ERROR_INT("&type not defined", __func__, 1); | |
| 871 *ptype = SEL_DONT_CARE; /* init */ | |
| 872 if (!sel) | |
| 873 return ERROR_INT("sel not defined", __func__, 1); | |
| 874 | |
| 875 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 876 for (i = 0; i < sy; i++) { | |
| 877 for (j = 0; j < sx; j++) { | |
| 878 if (i == cy && j == cx) { | |
| 879 selGetElement(sel, i, j, ptype); | |
| 880 return 0; | |
| 881 } | |
| 882 } | |
| 883 } | |
| 884 | |
| 885 return ERROR_INT("sel origin not found", __func__, 1); | |
| 886 } | |
| 887 | |
| 888 | |
| 889 /*! | |
| 890 * \brief selaGetBrickName() | |
| 891 * | |
| 892 * \param[in] sela | |
| 893 * \param[in] hsize, vsize of brick sel | |
| 894 * \return sel name new string, or NULL if no name or on error | |
| 895 */ | |
| 896 char * | |
| 897 selaGetBrickName(SELA *sela, | |
| 898 l_int32 hsize, | |
| 899 l_int32 vsize) | |
| 900 { | |
| 901 l_int32 i, nsels, sx, sy; | |
| 902 SEL *sel; | |
| 903 | |
| 904 if (!sela) | |
| 905 return (char *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 906 | |
| 907 nsels = selaGetCount(sela); | |
| 908 for (i = 0; i < nsels; i++) { | |
| 909 sel = selaGetSel(sela, i); | |
| 910 selGetParameters(sel, &sy, &sx, NULL, NULL); | |
| 911 if (hsize == sx && vsize == sy) | |
| 912 return stringNew(selGetName(sel)); | |
| 913 } | |
| 914 | |
| 915 return (char *)ERROR_PTR("sel not found", __func__, NULL); | |
| 916 } | |
| 917 | |
| 918 | |
| 919 /*! | |
| 920 * \brief selaGetCombName() | |
| 921 * | |
| 922 * \param[in] sela | |
| 923 * \param[in] size the product of sizes of the brick and comb parts | |
| 924 * \param[in] direction L_HORIZ, L_VERT | |
| 925 * \return sel name new string, or NULL if name not found or on error | |
| 926 * | |
| 927 * <pre> | |
| 928 * Notes: | |
| 929 * (1) Combs are by definition 1-dimensional, either horiz or vert. | |
| 930 * (2) Use this with comb Sels; e.g., from selaAddDwaCombs(). | |
| 931 * </pre> | |
| 932 */ | |
| 933 char * | |
| 934 selaGetCombName(SELA *sela, | |
| 935 l_int32 size, | |
| 936 l_int32 direction) | |
| 937 { | |
| 938 char *selname = NULL; | |
| 939 char combname[256]; | |
| 940 l_int32 i, nsels, sx, sy, found; | |
| 941 SEL *sel; | |
| 942 | |
| 943 if (!sela) | |
| 944 return (char *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 945 if (direction != L_HORIZ && direction != L_VERT) | |
| 946 return (char *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 947 | |
| 948 /* Derive the comb name we're looking for */ | |
| 949 if (direction == L_HORIZ) | |
| 950 snprintf(combname, sizeof(combname), "sel_comb_%dh", size); | |
| 951 else /* direction == L_VERT */ | |
| 952 snprintf(combname, sizeof(combname), "sel_comb_%dv", size); | |
| 953 | |
| 954 found = FALSE; | |
| 955 nsels = selaGetCount(sela); | |
| 956 for (i = 0; i < nsels; i++) { | |
| 957 sel = selaGetSel(sela, i); | |
| 958 selGetParameters(sel, &sy, &sx, NULL, NULL); | |
| 959 if (sy != 1 && sx != 1) /* 2-D; not a comb */ | |
| 960 continue; | |
| 961 selname = selGetName(sel); | |
| 962 if (!strcmp(selname, combname)) { | |
| 963 found = TRUE; | |
| 964 break; | |
| 965 } | |
| 966 } | |
| 967 | |
| 968 if (found) | |
| 969 return stringNew(selname); | |
| 970 else | |
| 971 return (char *)ERROR_PTR("sel not found", __func__, NULL); | |
| 972 } | |
| 973 | |
| 974 | |
| 975 /* --------- Function used to generate code in this file ---------- */ | |
| 976 #if 0 | |
| 977 static void selaComputeCompositeParameters(const char *fileout); | |
| 978 | |
| 979 /*! | |
| 980 * \brief selaComputeCompParameters() | |
| 981 * | |
| 982 * \param[in] fileout | |
| 983 * \return void | |
| 984 * | |
| 985 * <pre> | |
| 986 * Notes: | |
| 987 * (1) This static function was used to construct the comp_parameter_map[] | |
| 988 * array at the top of this file. It is static because it does | |
| 989 * not need to be called again. It remains here to show how | |
| 990 * the composite parameter map was computed. | |
| 991 * (2) The output file was pasted directly into comp_parameter_map[]. | |
| 992 * The composite parameter map is used to quickly determine | |
| 993 * the linear decomposition parameters and sel names. | |
| 994 * </pre> | |
| 995 */ | |
| 996 static void | |
| 997 selaComputeCompositeParameters(const char *fileout) | |
| 998 { | |
| 999 char *str, *nameh1, *nameh2, *namev1, *namev2; | |
| 1000 char buf[256]; | |
| 1001 l_int32 size, size1, size2, len; | |
| 1002 SARRAY *sa; | |
| 1003 SELA *selabasic, *selacomb; | |
| 1004 | |
| 1005 selabasic = selaAddBasic(NULL); | |
| 1006 selacomb = selaAddDwaCombs(NULL); | |
| 1007 sa = sarrayCreate(64); | |
| 1008 for (size = 2; size < 64; size++) { | |
| 1009 selectComposableSizes(size, &size1, &size2); | |
| 1010 nameh1 = selaGetBrickName(selabasic, size1, 1); | |
| 1011 namev1 = selaGetBrickName(selabasic, 1, size1); | |
| 1012 if (size2 > 1) { | |
| 1013 nameh2 = selaGetCombName(selacomb, size1 * size2, L_HORIZ); | |
| 1014 namev2 = selaGetCombName(selacomb, size1 * size2, L_VERT); | |
| 1015 } else { | |
| 1016 nameh2 = stringNew(""); | |
| 1017 namev2 = stringNew(""); | |
| 1018 } | |
| 1019 snprintf(buf, sizeof(buf), | |
| 1020 " { %d, %d, %d, \"%s\", \"%s\", \"%s\", \"%s\" },", | |
| 1021 size, size1, size2, nameh1, nameh2, namev1, namev2); | |
| 1022 sarrayAddString(sa, buf, L_COPY); | |
| 1023 LEPT_FREE(nameh1); | |
| 1024 LEPT_FREE(nameh2); | |
| 1025 LEPT_FREE(namev1); | |
| 1026 LEPT_FREE(namev2); | |
| 1027 } | |
| 1028 str = sarrayToString(sa, 1); | |
| 1029 len = strlen(str); | |
| 1030 l_binaryWrite(fileout, "w", str, len + 1); | |
| 1031 LEPT_FREE(str); | |
| 1032 sarrayDestroy(&sa); | |
| 1033 selaDestroy(&selabasic); | |
| 1034 selaDestroy(&selacomb); | |
| 1035 } | |
| 1036 #endif | |
| 1037 /* -------------------------------------------------------------------- */ | |
| 1038 | |
| 1039 | |
| 1040 /*! | |
| 1041 * \brief getCompositeParameters() | |
| 1042 * | |
| 1043 * \param[in] size | |
| 1044 * \param[out] psize1 [optional] brick factor size | |
| 1045 * \param[out] psize2 [optional] comb factor size | |
| 1046 * \param[out] pnameh1 [optional] name of horiz brick | |
| 1047 * \param[out] pnameh2 [optional] name of horiz comb | |
| 1048 * \param[out] pnamev1 [optional] name of vert brick | |
| 1049 * \param[out] pnamev2 [optional] name of vert comb | |
| 1050 * \return 0 if OK, 1 on error | |
| 1051 * | |
| 1052 * <pre> | |
| 1053 * Notes: | |
| 1054 * (1) This uses the big lookup table at the top of this file. | |
| 1055 * (2) All returned strings are copies that must be freed. | |
| 1056 * </pre> | |
| 1057 */ | |
| 1058 l_ok | |
| 1059 getCompositeParameters(l_int32 size, | |
| 1060 l_int32 *psize1, | |
| 1061 l_int32 *psize2, | |
| 1062 char **pnameh1, | |
| 1063 char **pnameh2, | |
| 1064 char **pnamev1, | |
| 1065 char **pnamev2) | |
| 1066 { | |
| 1067 l_int32 index; | |
| 1068 | |
| 1069 if (psize1) *psize1 = 0; | |
| 1070 if (psize2) *psize2 = 0; | |
| 1071 if (pnameh1) *pnameh1 = NULL; | |
| 1072 if (pnameh2) *pnameh2 = NULL; | |
| 1073 if (pnamev1) *pnamev1 = NULL; | |
| 1074 if (pnamev2) *pnamev2 = NULL; | |
| 1075 if (size < 2 || size > 63) | |
| 1076 return ERROR_INT("valid size range is {2 ... 63}", __func__, 1); | |
| 1077 index = size - 2; | |
| 1078 if (psize1) | |
| 1079 *psize1 = comp_parameter_map[index].size1; | |
| 1080 if (psize2) | |
| 1081 *psize2 = comp_parameter_map[index].size2; | |
| 1082 if (pnameh1) | |
| 1083 *pnameh1 = stringNew(comp_parameter_map[index].selnameh1); | |
| 1084 if (pnameh2) | |
| 1085 *pnameh2 = stringNew(comp_parameter_map[index].selnameh2); | |
| 1086 if (pnamev1) | |
| 1087 *pnamev1 = stringNew(comp_parameter_map[index].selnamev1); | |
| 1088 if (pnamev2) | |
| 1089 *pnamev2 = stringNew(comp_parameter_map[index].selnamev2); | |
| 1090 return 0; | |
| 1091 } | |
| 1092 | |
| 1093 | |
| 1094 /*! | |
| 1095 * \brief selaGetSelnames() | |
| 1096 * | |
| 1097 * \param[in] sela | |
| 1098 * \return sa of all sel names, or NULL on error | |
| 1099 */ | |
| 1100 SARRAY * | |
| 1101 selaGetSelnames(SELA *sela) | |
| 1102 { | |
| 1103 char *selname; | |
| 1104 l_int32 i, n; | |
| 1105 SEL *sel; | |
| 1106 SARRAY *sa; | |
| 1107 | |
| 1108 if (!sela) | |
| 1109 return (SARRAY *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 1110 if ((n = selaGetCount(sela)) == 0) | |
| 1111 return (SARRAY *)ERROR_PTR("no sels in sela", __func__, NULL); | |
| 1112 | |
| 1113 if ((sa = sarrayCreate(n)) == NULL) | |
| 1114 return (SARRAY *)ERROR_PTR("sa not made", __func__, NULL); | |
| 1115 for (i = 0; i < n; i++) { | |
| 1116 sel = selaGetSel(sela, i); | |
| 1117 selname = selGetName(sel); | |
| 1118 sarrayAddString(sa, selname, L_COPY); | |
| 1119 } | |
| 1120 | |
| 1121 return sa; | |
| 1122 } | |
| 1123 | |
| 1124 | |
| 1125 | |
| 1126 /*----------------------------------------------------------------------* | |
| 1127 * Max translations for erosion and hmt * | |
| 1128 *----------------------------------------------------------------------*/ | |
| 1129 /*! | |
| 1130 * \brief selFindMaxTranslations() | |
| 1131 * | |
| 1132 * \param[in] sel | |
| 1133 * \param[out] pxp, pyp, pxn, pyn max shifts | |
| 1134 * \return 0 if OK; 1 on error | |
| 1135 * | |
| 1136 * <pre> | |
| 1137 * Notes: | |
| 1138 These are the maximum shifts for the erosion operation. | |
| 1139 * For example, when j < cx, the shift of the image | |
| 1140 * is +x to the cx. This is a positive xp shift. | |
| 1141 * </pre> | |
| 1142 */ | |
| 1143 l_ok | |
| 1144 selFindMaxTranslations(SEL *sel, | |
| 1145 l_int32 *pxp, | |
| 1146 l_int32 *pyp, | |
| 1147 l_int32 *pxn, | |
| 1148 l_int32 *pyn) | |
| 1149 { | |
| 1150 l_int32 sx, sy, cx, cy, i, j; | |
| 1151 l_int32 maxxp, maxyp, maxxn, maxyn; | |
| 1152 | |
| 1153 if (!pxp || !pyp || !pxn || !pyn) | |
| 1154 return ERROR_INT("&xp (etc) defined", __func__, 1); | |
| 1155 *pxp = *pyp = *pxn = *pyn = 0; | |
| 1156 if (!sel) | |
| 1157 return ERROR_INT("sel not defined", __func__, 1); | |
| 1158 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 1159 | |
| 1160 maxxp = maxyp = maxxn = maxyn = 0; | |
| 1161 for (i = 0; i < sy; i++) { | |
| 1162 for (j = 0; j < sx; j++) { | |
| 1163 if (sel->data[i][j] == 1) { | |
| 1164 maxxp = L_MAX(maxxp, cx - j); | |
| 1165 maxyp = L_MAX(maxyp, cy - i); | |
| 1166 maxxn = L_MAX(maxxn, j - cx); | |
| 1167 maxyn = L_MAX(maxyn, i - cy); | |
| 1168 } | |
| 1169 } | |
| 1170 } | |
| 1171 | |
| 1172 *pxp = maxxp; | |
| 1173 *pyp = maxyp; | |
| 1174 *pxn = maxxn; | |
| 1175 *pyn = maxyn; | |
| 1176 | |
| 1177 return 0; | |
| 1178 } | |
| 1179 | |
| 1180 | |
| 1181 /*----------------------------------------------------------------------* | |
| 1182 * Rotation by multiples of 90 degrees * | |
| 1183 *----------------------------------------------------------------------*/ | |
| 1184 /*! | |
| 1185 * \brief selRotateOrth() | |
| 1186 * | |
| 1187 * \param[in] sel | |
| 1188 * \param[in] quads 0 - 4; number of 90 degree cw rotations | |
| 1189 * \return seld, or NULL on error | |
| 1190 */ | |
| 1191 SEL * | |
| 1192 selRotateOrth(SEL *sel, | |
| 1193 l_int32 quads) | |
| 1194 { | |
| 1195 l_int32 i, j, ni, nj, sx, sy, cx, cy, nsx, nsy, ncx, ncy, type; | |
| 1196 SEL *seld; | |
| 1197 | |
| 1198 if (!sel) | |
| 1199 return (SEL *)ERROR_PTR("sel not defined", __func__, NULL); | |
| 1200 if (quads < 0 || quads > 4) | |
| 1201 return (SEL *)ERROR_PTR("quads not in {0,1,2,3,4}", __func__, NULL); | |
| 1202 if (quads == 0 || quads == 4) | |
| 1203 return selCopy(sel); | |
| 1204 | |
| 1205 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 1206 if (quads == 1) { /* 90 degrees cw */ | |
| 1207 nsx = sy; | |
| 1208 nsy = sx; | |
| 1209 ncx = sy - cy - 1; | |
| 1210 ncy = cx; | |
| 1211 } else if (quads == 2) { /* 180 degrees cw */ | |
| 1212 nsx = sx; | |
| 1213 nsy = sy; | |
| 1214 ncx = sx - cx - 1; | |
| 1215 ncy = sy - cy - 1; | |
| 1216 } else { /* 270 degrees cw */ | |
| 1217 nsx = sy; | |
| 1218 nsy = sx; | |
| 1219 ncx = cy; | |
| 1220 ncy = sx - cx - 1; | |
| 1221 } | |
| 1222 seld = selCreateBrick(nsy, nsx, ncy, ncx, SEL_DONT_CARE); | |
| 1223 if (sel->name) | |
| 1224 seld->name = stringNew(sel->name); | |
| 1225 | |
| 1226 for (i = 0; i < sy; i++) { | |
| 1227 for (j = 0; j < sx; j++) { | |
| 1228 selGetElement(sel, i, j, &type); | |
| 1229 if (quads == 1) { | |
| 1230 ni = j; | |
| 1231 nj = sy - i - 1; | |
| 1232 } else if (quads == 2) { | |
| 1233 ni = sy - i - 1; | |
| 1234 nj = sx - j - 1; | |
| 1235 } else { /* quads == 3 */ | |
| 1236 ni = sx - j - 1; | |
| 1237 nj = i; | |
| 1238 } | |
| 1239 selSetElement(seld, ni, nj, type); | |
| 1240 } | |
| 1241 } | |
| 1242 | |
| 1243 return seld; | |
| 1244 } | |
| 1245 | |
| 1246 | |
| 1247 /*----------------------------------------------------------------------* | |
| 1248 * Sela and Sel serialized I/O * | |
| 1249 *----------------------------------------------------------------------*/ | |
| 1250 /*! | |
| 1251 * \brief selaRead() | |
| 1252 * | |
| 1253 * \param[in] fname filename | |
| 1254 * \return sela, or NULL on error | |
| 1255 */ | |
| 1256 SELA * | |
| 1257 selaRead(const char *fname) | |
| 1258 { | |
| 1259 FILE *fp; | |
| 1260 SELA *sela; | |
| 1261 | |
| 1262 if (!fname) | |
| 1263 return (SELA *)ERROR_PTR("fname not defined", __func__, NULL); | |
| 1264 | |
| 1265 if ((fp = fopenReadStream(fname)) == NULL) | |
| 1266 return (SELA *)ERROR_PTR_1("stream not opened", fname, __func__, NULL); | |
| 1267 if ((sela = selaReadStream(fp)) == NULL) { | |
| 1268 fclose(fp); | |
| 1269 return (SELA *)ERROR_PTR_1("sela not returned", fname, __func__, NULL); | |
| 1270 } | |
| 1271 fclose(fp); | |
| 1272 | |
| 1273 return sela; | |
| 1274 } | |
| 1275 | |
| 1276 | |
| 1277 /*! | |
| 1278 * \brief selaReadStream() | |
| 1279 * | |
| 1280 * \param[in] fp file stream | |
| 1281 * \return sela, or NULL on error | |
| 1282 */ | |
| 1283 SELA * | |
| 1284 selaReadStream(FILE *fp) | |
| 1285 { | |
| 1286 l_int32 i, n, version; | |
| 1287 SEL *sel; | |
| 1288 SELA *sela; | |
| 1289 | |
| 1290 if (!fp) | |
| 1291 return (SELA *)ERROR_PTR("stream not defined", __func__, NULL); | |
| 1292 | |
| 1293 if (fscanf(fp, "\nSela Version %d\n", &version) != 1) | |
| 1294 return (SELA *)ERROR_PTR("not a sela file", __func__, NULL); | |
| 1295 if (version != SEL_VERSION_NUMBER) | |
| 1296 return (SELA *)ERROR_PTR("invalid sel version", __func__, NULL); | |
| 1297 if (fscanf(fp, "Number of Sels = %d\n\n", &n) != 1) | |
| 1298 return (SELA *)ERROR_PTR("not a sela file", __func__, NULL); | |
| 1299 | |
| 1300 if ((sela = selaCreate(n)) == NULL) | |
| 1301 return (SELA *)ERROR_PTR("sela not made", __func__, NULL); | |
| 1302 sela->nalloc = n; | |
| 1303 | |
| 1304 for (i = 0; i < n; i++) { | |
| 1305 if ((sel = selReadStream(fp)) == NULL) { | |
| 1306 selaDestroy(&sela); | |
| 1307 return (SELA *)ERROR_PTR("sel not read", __func__, NULL); | |
| 1308 } | |
| 1309 selaAddSel(sela, sel, NULL, 0); | |
| 1310 } | |
| 1311 | |
| 1312 return sela; | |
| 1313 } | |
| 1314 | |
| 1315 | |
| 1316 /*! | |
| 1317 * \brief selRead() | |
| 1318 * | |
| 1319 * \param[in] fname filename | |
| 1320 * \return sel, or NULL on error | |
| 1321 */ | |
| 1322 SEL * | |
| 1323 selRead(const char *fname) | |
| 1324 { | |
| 1325 FILE *fp; | |
| 1326 SEL *sel; | |
| 1327 | |
| 1328 if (!fname) | |
| 1329 return (SEL *)ERROR_PTR("fname not defined", __func__, NULL); | |
| 1330 | |
| 1331 if ((fp = fopenReadStream(fname)) == NULL) | |
| 1332 return (SEL *)ERROR_PTR_1("stream not opened", fname, __func__, NULL); | |
| 1333 if ((sel = selReadStream(fp)) == NULL) { | |
| 1334 fclose(fp); | |
| 1335 return (SEL *)ERROR_PTR_1("sela not returned", fname, __func__, NULL); | |
| 1336 } | |
| 1337 fclose(fp); | |
| 1338 | |
| 1339 return sel; | |
| 1340 } | |
| 1341 | |
| 1342 | |
| 1343 /*! | |
| 1344 * \brief selReadStream() | |
| 1345 * | |
| 1346 * \param[in] fp file stream | |
| 1347 * \return sel, or NULL on error | |
| 1348 */ | |
| 1349 SEL * | |
| 1350 selReadStream(FILE *fp) | |
| 1351 { | |
| 1352 char selname[256]; | |
| 1353 char linebuf[256]; | |
| 1354 l_int32 sy, sx, cy, cx, i, j, version, ignore; | |
| 1355 SEL *sel; | |
| 1356 | |
| 1357 if (!fp) | |
| 1358 return (SEL *)ERROR_PTR("stream not defined", __func__, NULL); | |
| 1359 | |
| 1360 if (fscanf(fp, " Sel Version %d\n", &version) != 1) | |
| 1361 return (SEL *)ERROR_PTR("not a sel file", __func__, NULL); | |
| 1362 if (version != SEL_VERSION_NUMBER) | |
| 1363 return (SEL *)ERROR_PTR("invalid sel version", __func__, NULL); | |
| 1364 | |
| 1365 if (fgets(linebuf, sizeof(linebuf), fp) == NULL) | |
| 1366 return (SEL *)ERROR_PTR("error reading into linebuf", __func__, NULL); | |
| 1367 sscanf(linebuf, " ------ %200s ------", selname); | |
| 1368 | |
| 1369 if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", | |
| 1370 &sy, &sx, &cy, &cx) != 4) | |
| 1371 return (SEL *)ERROR_PTR("dimensions not read", __func__, NULL); | |
| 1372 | |
| 1373 if ((sel = selCreate(sy, sx, selname)) == NULL) | |
| 1374 return (SEL *)ERROR_PTR("sel not made", __func__, NULL); | |
| 1375 selSetOrigin(sel, cy, cx); | |
| 1376 | |
| 1377 for (i = 0; i < sy; i++) { | |
| 1378 ignore = fscanf(fp, " "); | |
| 1379 for (j = 0; j < sx; j++) | |
| 1380 ignore = fscanf(fp, "%1d", &sel->data[i][j]); | |
| 1381 ignore = fscanf(fp, "\n"); | |
| 1382 } | |
| 1383 ignore = fscanf(fp, "\n"); | |
| 1384 | |
| 1385 return sel; | |
| 1386 } | |
| 1387 | |
| 1388 | |
| 1389 /*! | |
| 1390 * \brief selaWrite() | |
| 1391 * | |
| 1392 * \param[in] fname filename | |
| 1393 * \param[in] sela | |
| 1394 * \return 0 if OK, 1 on error | |
| 1395 */ | |
| 1396 l_ok | |
| 1397 selaWrite(const char *fname, | |
| 1398 SELA *sela) | |
| 1399 { | |
| 1400 FILE *fp; | |
| 1401 | |
| 1402 if (!fname) | |
| 1403 return ERROR_INT("fname not defined", __func__, 1); | |
| 1404 if (!sela) | |
| 1405 return ERROR_INT("sela not defined", __func__, 1); | |
| 1406 | |
| 1407 if ((fp = fopenWriteStream(fname, "wb")) == NULL) | |
| 1408 return ERROR_INT_1("stream not opened", fname, __func__, 1); | |
| 1409 selaWriteStream(fp, sela); | |
| 1410 fclose(fp); | |
| 1411 | |
| 1412 return 0; | |
| 1413 } | |
| 1414 | |
| 1415 | |
| 1416 /*! | |
| 1417 * \brief selaWriteStream() | |
| 1418 * | |
| 1419 * \param[in] fp file stream | |
| 1420 * \param[in] sela | |
| 1421 * \return 0 if OK, 1 on error | |
| 1422 */ | |
| 1423 l_ok | |
| 1424 selaWriteStream(FILE *fp, | |
| 1425 SELA *sela) | |
| 1426 { | |
| 1427 l_int32 i, n; | |
| 1428 SEL *sel; | |
| 1429 | |
| 1430 if (!fp) | |
| 1431 return ERROR_INT("stream not defined", __func__, 1); | |
| 1432 if (!sela) | |
| 1433 return ERROR_INT("sela not defined", __func__, 1); | |
| 1434 | |
| 1435 n = selaGetCount(sela); | |
| 1436 fprintf(fp, "\nSela Version %d\n", SEL_VERSION_NUMBER); | |
| 1437 fprintf(fp, "Number of Sels = %d\n\n", n); | |
| 1438 for (i = 0; i < n; i++) { | |
| 1439 if ((sel = selaGetSel(sela, i)) == NULL) | |
| 1440 continue; | |
| 1441 selWriteStream(fp, sel); | |
| 1442 } | |
| 1443 return 0; | |
| 1444 } | |
| 1445 | |
| 1446 | |
| 1447 /*! | |
| 1448 * \brief selWrite() | |
| 1449 * | |
| 1450 * \param[in] fname filename | |
| 1451 * \param[in] sel | |
| 1452 * \return 0 if OK, 1 on error | |
| 1453 */ | |
| 1454 l_ok | |
| 1455 selWrite(const char *fname, | |
| 1456 SEL *sel) | |
| 1457 { | |
| 1458 FILE *fp; | |
| 1459 | |
| 1460 if (!fname) | |
| 1461 return ERROR_INT("fname not defined", __func__, 1); | |
| 1462 if (!sel) | |
| 1463 return ERROR_INT("sel not defined", __func__, 1); | |
| 1464 | |
| 1465 if ((fp = fopenWriteStream(fname, "wb")) == NULL) | |
| 1466 return ERROR_INT_1("stream not opened", fname, __func__, 1); | |
| 1467 selWriteStream(fp, sel); | |
| 1468 fclose(fp); | |
| 1469 | |
| 1470 return 0; | |
| 1471 } | |
| 1472 | |
| 1473 | |
| 1474 /*! | |
| 1475 * \brief selWriteStream() | |
| 1476 * | |
| 1477 * \param[in] fp file stream | |
| 1478 * \param[in] sel | |
| 1479 * \return 0 if OK, 1 on error | |
| 1480 */ | |
| 1481 l_ok | |
| 1482 selWriteStream(FILE *fp, | |
| 1483 SEL *sel) | |
| 1484 { | |
| 1485 l_int32 sx, sy, cx, cy, i, j; | |
| 1486 | |
| 1487 if (!fp) | |
| 1488 return ERROR_INT("stream not defined", __func__, 1); | |
| 1489 if (!sel) | |
| 1490 return ERROR_INT("sel not defined", __func__, 1); | |
| 1491 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 1492 | |
| 1493 fprintf(fp, " Sel Version %d\n", SEL_VERSION_NUMBER); | |
| 1494 fprintf(fp, " ------ %s ------\n", selGetName(sel)); | |
| 1495 fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); | |
| 1496 for (i = 0; i < sy; i++) { | |
| 1497 fprintf(fp, " "); | |
| 1498 for (j = 0; j < sx; j++) | |
| 1499 fprintf(fp, "%d", sel->data[i][j]); | |
| 1500 fprintf(fp, "\n"); | |
| 1501 } | |
| 1502 fprintf(fp, "\n"); | |
| 1503 | |
| 1504 return 0; | |
| 1505 } | |
| 1506 | |
| 1507 | |
| 1508 /*----------------------------------------------------------------------* | |
| 1509 * Building custom hit-miss sels from compiled strings * | |
| 1510 *----------------------------------------------------------------------*/ | |
| 1511 /*! | |
| 1512 * \brief selCreateFromString() | |
| 1513 * | |
| 1514 * \param[in] text | |
| 1515 * \param[in] h, w height, width | |
| 1516 * \param[in] name [optional] sel name; can be null | |
| 1517 * \return sel of the given size, or NULL on error | |
| 1518 * | |
| 1519 * <pre> | |
| 1520 * Notes: | |
| 1521 * (1) The text is an array of chars (in row-major order) where | |
| 1522 * each char can be one of the following: | |
| 1523 * 'x': hit | |
| 1524 * 'o': miss | |
| 1525 * ' ': don't-care | |
| 1526 * (2) When the origin falls on a hit or miss, use an upper case | |
| 1527 * char (e.g., 'X' or 'O') to indicate it. When the origin | |
| 1528 * falls on a don't-care, indicate this with a 'C'. | |
| 1529 * The string must have exactly one origin specified. | |
| 1530 * (3) The advantage of this method is that the text can be input | |
| 1531 * in a format that shows the 2D layout of the Sel; e.g., | |
| 1532 * \code | |
| 1533 * static const char *seltext = "x " | |
| 1534 * "x Oo " | |
| 1535 * "x " | |
| 1536 * "xxxxx"; | |
| 1537 * \endcode | |
| 1538 * </pre> | |
| 1539 */ | |
| 1540 SEL * | |
| 1541 selCreateFromString(const char *text, | |
| 1542 l_int32 h, | |
| 1543 l_int32 w, | |
| 1544 const char *name) | |
| 1545 { | |
| 1546 SEL *sel; | |
| 1547 l_int32 y, x, norig; | |
| 1548 char ch; | |
| 1549 | |
| 1550 if (!text || text[0] == '\0') | |
| 1551 return (SEL *)ERROR_PTR("text undefined or empty", __func__, NULL); | |
| 1552 if (h < 1) | |
| 1553 return (SEL *)ERROR_PTR("height must be > 0", __func__, NULL); | |
| 1554 if (w < 1) | |
| 1555 return (SEL *)ERROR_PTR("width must be > 0", __func__, NULL); | |
| 1556 if (strlen(text) != (size_t)w * h) | |
| 1557 return (SEL *)ERROR_PTR("text size != w * h", __func__, NULL); | |
| 1558 | |
| 1559 sel = selCreate(h, w, name); | |
| 1560 norig = 0; | |
| 1561 for (y = 0; y < h; ++y) { | |
| 1562 for (x = 0; x < w; ++x) { | |
| 1563 ch = *(text++); | |
| 1564 switch (ch) | |
| 1565 { | |
| 1566 case 'X': | |
| 1567 norig++; | |
| 1568 selSetOrigin(sel, y, x); | |
| 1569 /* fall through */ | |
| 1570 case 'x': | |
| 1571 selSetElement(sel, y, x, SEL_HIT); | |
| 1572 break; | |
| 1573 | |
| 1574 case 'O': | |
| 1575 norig++; | |
| 1576 selSetOrigin(sel, y, x); | |
| 1577 /* fall through */ | |
| 1578 case 'o': | |
| 1579 selSetElement(sel, y, x, SEL_MISS); | |
| 1580 break; | |
| 1581 | |
| 1582 case 'C': | |
| 1583 norig++; | |
| 1584 selSetOrigin(sel, y, x); | |
| 1585 /* fall through */ | |
| 1586 case ' ': | |
| 1587 selSetElement(sel, y, x, SEL_DONT_CARE); | |
| 1588 break; | |
| 1589 | |
| 1590 case '\n': | |
| 1591 /* ignored */ | |
| 1592 continue; | |
| 1593 | |
| 1594 default: | |
| 1595 selDestroy(&sel); | |
| 1596 return (SEL *)ERROR_PTR("unknown char", __func__, NULL); | |
| 1597 } | |
| 1598 } | |
| 1599 } | |
| 1600 if (norig != 1) { | |
| 1601 L_ERROR("Exactly one origin must be specified; this string has %d\n", | |
| 1602 __func__, norig); | |
| 1603 selDestroy(&sel); | |
| 1604 } | |
| 1605 | |
| 1606 return sel; | |
| 1607 } | |
| 1608 | |
| 1609 | |
| 1610 /*! | |
| 1611 * \brief selPrintToString() | |
| 1612 * | |
| 1613 * \param[in] sel | |
| 1614 * \return str string; caller must free | |
| 1615 * | |
| 1616 * <pre> | |
| 1617 * Notes: | |
| 1618 * (1) This is an inverse function of selCreateFromString. | |
| 1619 * It prints a textual representation of the SEL to a malloc'd | |
| 1620 * string. The format is the same as selCreateFromString | |
| 1621 * except that newlines are inserted into the output | |
| 1622 * between rows. | |
| 1623 * (2) This is useful for debugging. However, if you want to | |
| 1624 * save some Sels in a file, put them in a Sela and write | |
| 1625 * them out with selaWrite(). They can then be read in | |
| 1626 * with selaRead(). | |
| 1627 * </pre> | |
| 1628 */ | |
| 1629 char * | |
| 1630 selPrintToString(SEL *sel) | |
| 1631 { | |
| 1632 char is_center; | |
| 1633 char *str, *strptr; | |
| 1634 l_int32 type; | |
| 1635 l_int32 sx, sy, cx, cy, x, y; | |
| 1636 | |
| 1637 if (!sel) | |
| 1638 return (char *)ERROR_PTR("sel not defined", __func__, NULL); | |
| 1639 | |
| 1640 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 1641 if ((str = (char *)LEPT_CALLOC(1, sy * (sx + 1) + 1)) == NULL) | |
| 1642 return (char *)ERROR_PTR("calloc fail for str", __func__, NULL); | |
| 1643 strptr = str; | |
| 1644 | |
| 1645 for (y = 0; y < sy; ++y) { | |
| 1646 for (x = 0; x < sx; ++x) { | |
| 1647 selGetElement(sel, y, x, &type); | |
| 1648 is_center = (x == cx && y == cy); | |
| 1649 switch (type) { | |
| 1650 case SEL_HIT: | |
| 1651 *(strptr++) = is_center ? 'X' : 'x'; | |
| 1652 break; | |
| 1653 case SEL_MISS: | |
| 1654 *(strptr++) = is_center ? 'O' : 'o'; | |
| 1655 break; | |
| 1656 case SEL_DONT_CARE: | |
| 1657 *(strptr++) = is_center ? 'C' : ' '; | |
| 1658 break; | |
| 1659 } | |
| 1660 } | |
| 1661 *(strptr++) = '\n'; | |
| 1662 } | |
| 1663 | |
| 1664 return str; | |
| 1665 } | |
| 1666 | |
| 1667 | |
| 1668 /*----------------------------------------------------------------------* | |
| 1669 * Building custom hit-miss sels from a simple file format * | |
| 1670 *----------------------------------------------------------------------*/ | |
| 1671 /*! | |
| 1672 * \brief selaCreateFromFile() | |
| 1673 * | |
| 1674 * \param[in] filename | |
| 1675 * \return sela, or NULL on error | |
| 1676 * | |
| 1677 * <pre> | |
| 1678 * Notes: | |
| 1679 * (1) The file contains a sequence of Sel descriptions. | |
| 1680 * (2) Each Sel is formatted as follows: | |
| 1681 * ~ Any number of comment lines starting with '#' are ignored | |
| 1682 * ~ The next line contains the selname | |
| 1683 * ~ The next lines contain the Sel data. They must be | |
| 1684 * formatted similarly to the string format in | |
| 1685 * selCreateFromString(), with each line beginning and | |
| 1686 * ending with a double-quote, and showing the 2D layout. | |
| 1687 * ~ Each Sel ends when a blank line, a comment line, or | |
| 1688 * the end of file is reached. | |
| 1689 * (3) See selCreateFromString() for a description of the string | |
| 1690 * format for the Sel data. As an example, here are the lines | |
| 1691 * of is a valid file for a single Sel. In the file, all lines | |
| 1692 * are left-justified: | |
| 1693 * # diagonal sel | |
| 1694 * sel_5diag | |
| 1695 * "x " | |
| 1696 * " x " | |
| 1697 * " X " | |
| 1698 * " x " | |
| 1699 * " x" | |
| 1700 * </pre> | |
| 1701 */ | |
| 1702 SELA * | |
| 1703 selaCreateFromFile(const char *filename) | |
| 1704 { | |
| 1705 char *filestr, *line; | |
| 1706 l_int32 i, n, first, last, nsel, insel; | |
| 1707 size_t nbytes; | |
| 1708 NUMA *nafirst, *nalast; | |
| 1709 SARRAY *sa; | |
| 1710 SEL *sel; | |
| 1711 SELA *sela; | |
| 1712 | |
| 1713 if (!filename) | |
| 1714 return (SELA *)ERROR_PTR("filename not defined", __func__, NULL); | |
| 1715 | |
| 1716 filestr = (char *)l_binaryRead(filename, &nbytes); | |
| 1717 sa = sarrayCreateLinesFromString(filestr, 1); | |
| 1718 LEPT_FREE(filestr); | |
| 1719 n = sarrayGetCount(sa); | |
| 1720 sela = selaCreate(0); | |
| 1721 | |
| 1722 /* Find the start and end lines for each Sel. | |
| 1723 * We allow the "blank" lines to be null strings or | |
| 1724 * to have standard whitespace (' ','\t',\'n') or be '#'. */ | |
| 1725 nafirst = numaCreate(0); | |
| 1726 nalast = numaCreate(0); | |
| 1727 insel = FALSE; | |
| 1728 for (i = 0; i < n; i++) { | |
| 1729 line = sarrayGetString(sa, i, L_NOCOPY); | |
| 1730 if (!insel && | |
| 1731 (line[0] != '\0' && line[0] != ' ' && | |
| 1732 line[0] != '\t' && line[0] != '\n' && line[0] != '#')) { | |
| 1733 numaAddNumber(nafirst, i); | |
| 1734 insel = TRUE; | |
| 1735 continue; | |
| 1736 } | |
| 1737 if (insel && | |
| 1738 (line[0] == '\0' || line[0] == ' ' || | |
| 1739 line[0] == '\t' || line[0] == '\n' || line[0] == '#')) { | |
| 1740 numaAddNumber(nalast, i - 1); | |
| 1741 insel = FALSE; | |
| 1742 continue; | |
| 1743 } | |
| 1744 } | |
| 1745 if (insel) /* fell off the end of the file */ | |
| 1746 numaAddNumber(nalast, n - 1); | |
| 1747 | |
| 1748 /* Extract sels */ | |
| 1749 nsel = numaGetCount(nafirst); | |
| 1750 for (i = 0; i < nsel; i++) { | |
| 1751 numaGetIValue(nafirst, i, &first); | |
| 1752 numaGetIValue(nalast, i, &last); | |
| 1753 if ((sel = selCreateFromSArray(sa, first, last)) == NULL) { | |
| 1754 lept_stderr("Error reading sel from %d to %d\n", first, last); | |
| 1755 selaDestroy(&sela); | |
| 1756 sarrayDestroy(&sa); | |
| 1757 numaDestroy(&nafirst); | |
| 1758 numaDestroy(&nalast); | |
| 1759 return (SELA *)ERROR_PTR("bad sela file", __func__, NULL); | |
| 1760 } | |
| 1761 selaAddSel(sela, sel, NULL, 0); | |
| 1762 } | |
| 1763 | |
| 1764 numaDestroy(&nafirst); | |
| 1765 numaDestroy(&nalast); | |
| 1766 sarrayDestroy(&sa); | |
| 1767 return sela; | |
| 1768 } | |
| 1769 | |
| 1770 | |
| 1771 /*! | |
| 1772 * \brief selCreateFromSArray() | |
| 1773 * | |
| 1774 * \param[in] sa | |
| 1775 * \param[in] first line of sarray where Sel begins | |
| 1776 * \param[in] last line of sarray where Sel ends | |
| 1777 * \return sela, or NULL on error | |
| 1778 * | |
| 1779 * <pre> | |
| 1780 * Notes: | |
| 1781 * (1) The Sel contains the following lines: | |
| 1782 * ~ The first line is the selname | |
| 1783 * ~ The remaining lines contain the Sel data. They must | |
| 1784 * be formatted similarly to the string format in | |
| 1785 * selCreateFromString(), with each line beginning and | |
| 1786 * ending with a double-quote, and showing the 2D layout. | |
| 1787 * ~ 'last' gives the last line in the Sel data. | |
| 1788 * (2) See selCreateFromString() for a description of the string | |
| 1789 * format for the Sel data. As an example, here are the lines | |
| 1790 * of is a valid file for a single Sel. In the file, all lines | |
| 1791 * are left-justified: | |
| 1792 * # diagonal sel | |
| 1793 * sel_5diag | |
| 1794 * "x " | |
| 1795 * " x " | |
| 1796 * " X " | |
| 1797 * " x " | |
| 1798 * " x" | |
| 1799 * </pre> | |
| 1800 */ | |
| 1801 static SEL * | |
| 1802 selCreateFromSArray(SARRAY *sa, | |
| 1803 l_int32 first, | |
| 1804 l_int32 last) | |
| 1805 { | |
| 1806 char ch; | |
| 1807 char *name, *line; | |
| 1808 l_int32 n, len, i, w, h, y, x; | |
| 1809 SEL *sel; | |
| 1810 | |
| 1811 if (!sa) | |
| 1812 return (SEL *)ERROR_PTR("sa not defined", __func__, NULL); | |
| 1813 n = sarrayGetCount(sa); | |
| 1814 if (first < 0 || first >= n || last <= first || last >= n) | |
| 1815 return (SEL *)ERROR_PTR("invalid range", __func__, NULL); | |
| 1816 | |
| 1817 name = sarrayGetString(sa, first, L_NOCOPY); | |
| 1818 h = last - first; | |
| 1819 line = sarrayGetString(sa, first + 1, L_NOCOPY); | |
| 1820 len = strlen(line); | |
| 1821 if (line[0] != '"' || line[len - 1] != '"') | |
| 1822 return (SEL *)ERROR_PTR("invalid format", __func__, NULL); | |
| 1823 w = len - 2; | |
| 1824 if ((sel = selCreate(h, w, name)) == NULL) | |
| 1825 return (SEL *)ERROR_PTR("sel not made", __func__, NULL); | |
| 1826 for (i = first + 1; i <= last; i++) { | |
| 1827 line = sarrayGetString(sa, i, L_NOCOPY); | |
| 1828 y = i - first - 1; | |
| 1829 for (x = 0; x < w; ++x) { | |
| 1830 ch = line[x + 1]; /* skip the leading double-quote */ | |
| 1831 switch (ch) | |
| 1832 { | |
| 1833 case 'X': | |
| 1834 selSetOrigin(sel, y, x); /* set origin and hit */ | |
| 1835 /* fall through */ | |
| 1836 case 'x': | |
| 1837 selSetElement(sel, y, x, SEL_HIT); | |
| 1838 break; | |
| 1839 | |
| 1840 case 'O': | |
| 1841 selSetOrigin(sel, y, x); /* set origin and miss */ | |
| 1842 /* fall through */ | |
| 1843 case 'o': | |
| 1844 selSetElement(sel, y, x, SEL_MISS); | |
| 1845 break; | |
| 1846 | |
| 1847 case 'C': | |
| 1848 selSetOrigin(sel, y, x); /* set origin and don't-care */ | |
| 1849 /* fall through */ | |
| 1850 case ' ': | |
| 1851 selSetElement(sel, y, x, SEL_DONT_CARE); | |
| 1852 break; | |
| 1853 | |
| 1854 default: | |
| 1855 selDestroy(&sel); | |
| 1856 return (SEL *)ERROR_PTR("unknown char", __func__, NULL); | |
| 1857 } | |
| 1858 } | |
| 1859 } | |
| 1860 | |
| 1861 return sel; | |
| 1862 } | |
| 1863 | |
| 1864 | |
| 1865 /*----------------------------------------------------------------------* | |
| 1866 * Making hit-only SELs from Pta and Pix * | |
| 1867 *----------------------------------------------------------------------*/ | |
| 1868 /*! | |
| 1869 * \brief selCreateFromPta() | |
| 1870 * | |
| 1871 * \param[in] pta | |
| 1872 * \param[in] cy, cx origin of sel | |
| 1873 * \param[in] name [optional] sel name; can be null | |
| 1874 * \return sel of minimum required size, or NULL on error | |
| 1875 * | |
| 1876 * <pre> | |
| 1877 * Notes: | |
| 1878 * (1) The origin and all points in the pta must be positive. | |
| 1879 * </pre> | |
| 1880 */ | |
| 1881 SEL * | |
| 1882 selCreateFromPta(PTA *pta, | |
| 1883 l_int32 cy, | |
| 1884 l_int32 cx, | |
| 1885 const char *name) | |
| 1886 { | |
| 1887 l_int32 i, n, x, y, w, h; | |
| 1888 BOX *box; | |
| 1889 SEL *sel; | |
| 1890 | |
| 1891 if (!pta) | |
| 1892 return (SEL *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 1893 if (cy < 0 || cx < 0) | |
| 1894 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL); | |
| 1895 n = ptaGetCount(pta); | |
| 1896 if (n == 0) | |
| 1897 return (SEL *)ERROR_PTR("no pts in pta", __func__, NULL); | |
| 1898 | |
| 1899 box = ptaGetBoundingRegion(pta); | |
| 1900 boxGetGeometry(box, &x, &y, &w, &h); | |
| 1901 boxDestroy(&box); | |
| 1902 if (x < 0 || y < 0) | |
| 1903 return (SEL *)ERROR_PTR("not all x and y >= 0", __func__, NULL); | |
| 1904 | |
| 1905 sel = selCreate(y + h, x + w, name); | |
| 1906 selSetOrigin(sel, cy, cx); | |
| 1907 for (i = 0; i < n; i++) { | |
| 1908 ptaGetIPt(pta, i, &x, &y); | |
| 1909 selSetElement(sel, y, x, SEL_HIT); | |
| 1910 } | |
| 1911 | |
| 1912 return sel; | |
| 1913 } | |
| 1914 | |
| 1915 | |
| 1916 /*! | |
| 1917 * \brief selCreateFromPix() | |
| 1918 * | |
| 1919 * \param[in] pix | |
| 1920 * \param[in] cy, cx origin of sel | |
| 1921 * \param[in] name [optional] sel name; can be null | |
| 1922 * \return sel, or NULL on error | |
| 1923 * | |
| 1924 * <pre> | |
| 1925 * Notes: | |
| 1926 * (1) The origin must be positive. | |
| 1927 * (2) The pix must not exceed MaxPixTemplateSize in either dimension. | |
| 1928 * and the total number of hits must not exceed MaxPixTemplateHits. | |
| 1929 * </pre> | |
| 1930 */ | |
| 1931 SEL * | |
| 1932 selCreateFromPix(PIX *pix, | |
| 1933 l_int32 cy, | |
| 1934 l_int32 cx, | |
| 1935 const char *name) | |
| 1936 { | |
| 1937 SEL *sel; | |
| 1938 l_int32 i, j, w, h, d, nhits; | |
| 1939 l_uint32 val; | |
| 1940 | |
| 1941 if (!pix) | |
| 1942 return (SEL *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 1943 if (cy < 0 || cx < 0) | |
| 1944 return (SEL *)ERROR_PTR("(cy, cx) not both >= 0", __func__, NULL); | |
| 1945 pixGetDimensions(pix, &w, &h, &d); | |
| 1946 if (d != 1) | |
| 1947 return (SEL *)ERROR_PTR("pix not 1 bpp", __func__, NULL); | |
| 1948 if (w > MaxPixTemplateSize || h > MaxPixTemplateSize) { | |
| 1949 L_ERROR("pix template too large (w = %d, h = %d)\n", __func__, w, h); | |
| 1950 return NULL; | |
| 1951 } | |
| 1952 if (w > MaxPixTemplateSize / 5 || h > MaxPixTemplateSize / 5) | |
| 1953 L_WARNING("large pix template: w = %d, h = %d\n", __func__, w, h); | |
| 1954 pixCountPixels(pix, &nhits, NULL); | |
| 1955 if (nhits > MaxPixTemplateHits) { | |
| 1956 L_ERROR("too many hits (%d) in pix template\n", __func__, nhits); | |
| 1957 return NULL; | |
| 1958 } | |
| 1959 if (nhits > MaxPixTemplateHits / 5) | |
| 1960 L_WARNING("many hits (%d) in pix template\n", __func__, nhits); | |
| 1961 | |
| 1962 sel = selCreate(h, w, name); | |
| 1963 selSetOrigin(sel, cy, cx); | |
| 1964 for (i = 0; i < h; i++) { | |
| 1965 for (j = 0; j < w; j++) { | |
| 1966 pixGetPixel(pix, j, i, &val); | |
| 1967 if (val) | |
| 1968 selSetElement(sel, i, j, SEL_HIT); | |
| 1969 } | |
| 1970 } | |
| 1971 | |
| 1972 return sel; | |
| 1973 } | |
| 1974 | |
| 1975 | |
| 1976 /*----------------------------------------------------------------------* | |
| 1977 * Making hit-miss sels from color Pix and image files * | |
| 1978 *----------------------------------------------------------------------*/ | |
| 1979 /*! | |
| 1980 * | |
| 1981 * selReadFromColorImage() | |
| 1982 * | |
| 1983 * \param[in] pathname | |
| 1984 * \return sel if OK; NULL on error | |
| 1985 * | |
| 1986 * <pre> | |
| 1987 * Notes: | |
| 1988 * (1) Loads an image from a file and creates a (hit-miss) sel. | |
| 1989 * (2) The sel name is taken from the pathname without the directory | |
| 1990 * and extension. | |
| 1991 * </pre> | |
| 1992 */ | |
| 1993 SEL * | |
| 1994 selReadFromColorImage(const char *pathname) | |
| 1995 { | |
| 1996 PIX *pix; | |
| 1997 SEL *sel; | |
| 1998 char *basename, *selname; | |
| 1999 | |
| 2000 splitPathAtExtension (pathname, &basename, NULL); | |
| 2001 splitPathAtDirectory (basename, NULL, &selname); | |
| 2002 LEPT_FREE(basename); | |
| 2003 | |
| 2004 if ((pix = pixRead(pathname)) == NULL) { | |
| 2005 LEPT_FREE(selname); | |
| 2006 return (SEL *)ERROR_PTR("pix not returned", __func__, NULL); | |
| 2007 } | |
| 2008 if ((sel = selCreateFromColorPix(pix, selname)) == NULL) | |
| 2009 L_ERROR("sel not made\n", __func__); | |
| 2010 | |
| 2011 LEPT_FREE(selname); | |
| 2012 pixDestroy(&pix); | |
| 2013 return sel; | |
| 2014 } | |
| 2015 | |
| 2016 | |
| 2017 /*! | |
| 2018 * | |
| 2019 * selCreateFromColorPix() | |
| 2020 * | |
| 2021 * \param[in] pixs cmapped or rgb | |
| 2022 * \param[in] selname [optional] sel name; can be null | |
| 2023 * \return sel if OK, NULL on error | |
| 2024 * | |
| 2025 * <pre> | |
| 2026 * Notes: | |
| 2027 * (1) The sel size is given by the size of pixs. | |
| 2028 * (2) In pixs, hits are represented by green pixels, misses by red | |
| 2029 * pixels, and don't-cares by white pixels. | |
| 2030 * (3) In pixs, there may be no misses, but there must be at least 1 hit. | |
| 2031 * (4) At most there can be only one origin pixel, which is optionally | |
| 2032 * specified by using a lower-intensity pixel: | |
| 2033 * if a hit: dark green | |
| 2034 * if a miss: dark red | |
| 2035 * if a don't care: gray | |
| 2036 * If there is no such pixel, the origin defaults to the approximate | |
| 2037 * center of the sel. | |
| 2038 * </pre> | |
| 2039 */ | |
| 2040 SEL * | |
| 2041 selCreateFromColorPix(PIX *pixs, | |
| 2042 const char *selname) | |
| 2043 { | |
| 2044 PIXCMAP *cmap; | |
| 2045 SEL *sel; | |
| 2046 l_int32 hascolor, num_origins, nohits; | |
| 2047 l_int32 w, h, d, i, j, red, green, blue; | |
| 2048 l_uint32 pixval; | |
| 2049 | |
| 2050 if (!pixs) | |
| 2051 return (SEL *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2052 | |
| 2053 hascolor = FALSE; | |
| 2054 cmap = pixGetColormap(pixs); | |
| 2055 if (cmap) | |
| 2056 pixcmapHasColor(cmap, &hascolor); | |
| 2057 pixGetDimensions(pixs, &w, &h, &d); | |
| 2058 if (hascolor == FALSE && d != 32) | |
| 2059 return (SEL *)ERROR_PTR("pixs has no color", __func__, NULL); | |
| 2060 | |
| 2061 if ((sel = selCreate (h, w, NULL)) == NULL) | |
| 2062 return (SEL *)ERROR_PTR ("sel not made", __func__, NULL); | |
| 2063 selSetOrigin (sel, h / 2, w / 2); /* default */ | |
| 2064 selSetName(sel, selname); | |
| 2065 | |
| 2066 num_origins = 0; | |
| 2067 nohits = TRUE; | |
| 2068 for (i = 0; i < h; i++) { | |
| 2069 for (j = 0; j < w; j++) { | |
| 2070 pixGetPixel (pixs, j, i, &pixval); | |
| 2071 | |
| 2072 if (cmap) { | |
| 2073 pixcmapGetColor (cmap, pixval, &red, &green, &blue); | |
| 2074 } else { | |
| 2075 red = GET_DATA_BYTE (&pixval, COLOR_RED); | |
| 2076 green = GET_DATA_BYTE (&pixval, COLOR_GREEN); | |
| 2077 blue = GET_DATA_BYTE (&pixval, COLOR_BLUE); | |
| 2078 } | |
| 2079 | |
| 2080 if (red < 255 && green < 255 && blue < 255) { | |
| 2081 num_origins++; | |
| 2082 if (num_origins == 1) /* first one found */ | |
| 2083 selSetOrigin (sel, i, j); | |
| 2084 if (num_origins == 2) | |
| 2085 L_WARNING("multiple origins in sel image\n", __func__); | |
| 2086 } | |
| 2087 if (!red && green && !blue) { | |
| 2088 nohits = FALSE; | |
| 2089 selSetElement (sel, i, j, SEL_HIT); | |
| 2090 } else if (red && !green && !blue) { | |
| 2091 selSetElement (sel, i, j, SEL_MISS); | |
| 2092 } else if (red && green && blue) { | |
| 2093 selSetElement (sel, i, j, SEL_DONT_CARE); | |
| 2094 } else { | |
| 2095 selDestroy(&sel); | |
| 2096 return (SEL *)ERROR_PTR("invalid color", __func__, NULL); | |
| 2097 } | |
| 2098 } | |
| 2099 } | |
| 2100 | |
| 2101 if (nohits) { | |
| 2102 selDestroy(&sel); | |
| 2103 return (SEL *)ERROR_PTR("no hits in sel", __func__, NULL); | |
| 2104 } | |
| 2105 return sel; | |
| 2106 } | |
| 2107 | |
| 2108 | |
| 2109 /*! | |
| 2110 * | |
| 2111 * selaCreateFromColorPixa() | |
| 2112 * | |
| 2113 * \param[in] pixa color pixa representing the sels | |
| 2114 * \param[in] sa sarray of sel names | |
| 2115 * \return sel if OK, NULL on error | |
| 2116 * | |
| 2117 * <pre> | |
| 2118 * Notes: | |
| 2119 * (1) See notes in selCreateFromColorPix() | |
| 2120 * (2) sa is required because all sels that are put in a sela | |
| 2121 * must have a name. | |
| 2122 * </pre> | |
| 2123 */ | |
| 2124 SELA * | |
| 2125 selaCreateFromColorPixa(PIXA *pixa, | |
| 2126 SARRAY *sa) | |
| 2127 { | |
| 2128 char *str; | |
| 2129 l_int32 i, n; | |
| 2130 PIX *pix; | |
| 2131 SEL *sel; | |
| 2132 SELA *sela; | |
| 2133 | |
| 2134 if (!pixa) | |
| 2135 return (SELA *)ERROR_PTR("pixa not defined", __func__, NULL); | |
| 2136 if (!sa) | |
| 2137 return (SELA *)ERROR_PTR("sa of sel names not defined", __func__, NULL); | |
| 2138 | |
| 2139 n = pixaGetCount(pixa); | |
| 2140 if ((sela = selaCreate(n)) == NULL) | |
| 2141 return (SELA *)ERROR_PTR("sela not allocated", __func__, NULL); | |
| 2142 for (i = 0; i < n; i++) { | |
| 2143 pix = pixaGetPix(pixa, i, L_CLONE); | |
| 2144 str = sarrayGetString(sa, i, L_NOCOPY); | |
| 2145 sel = selCreateFromColorPix(pix, str); | |
| 2146 selaAddSel(sela, sel, NULL, L_INSERT); | |
| 2147 pixDestroy(&pix); | |
| 2148 } | |
| 2149 return sela; | |
| 2150 } | |
| 2151 | |
| 2152 | |
| 2153 /*----------------------------------------------------------------------* | |
| 2154 * Printable display of sel * | |
| 2155 *----------------------------------------------------------------------*/ | |
| 2156 /*! | |
| 2157 * \brief selDisplayInPix() | |
| 2158 * | |
| 2159 * \param[in] sel | |
| 2160 * \param[in] size of grid interiors; odd; minimum size of 13 is enforced | |
| 2161 * \param[in] gthick grid thickness; minimum size of 2 is enforced | |
| 2162 * \return pix display of sel, or NULL on error | |
| 2163 * | |
| 2164 * <pre> | |
| 2165 * Notes: | |
| 2166 * (1) This gives a visual representation of a general (hit-miss) sel. | |
| 2167 * (2) The empty sel is represented by a grid of intersecting lines. | |
| 2168 * (3) Three different patterns are generated for the sel elements: | |
| 2169 * ~ hit (solid black circle) | |
| 2170 * ~ miss (black ring; inner radius is radius2) | |
| 2171 * ~ origin (cross, XORed with whatever is there) | |
| 2172 * </pre> | |
| 2173 */ | |
| 2174 PIX * | |
| 2175 selDisplayInPix(SEL *sel, | |
| 2176 l_int32 size, | |
| 2177 l_int32 gthick) | |
| 2178 { | |
| 2179 l_int32 i, j, w, h, sx, sy, cx, cy, type, width; | |
| 2180 l_int32 radius1, radius2, shift1, shift2, x0, y0; | |
| 2181 PIX *pixd, *pix2, *pixh, *pixm, *pixorig; | |
| 2182 PTA *pta1, *pta2, *pta1t, *pta2t; | |
| 2183 | |
| 2184 if (!sel) | |
| 2185 return (PIX *)ERROR_PTR("sel not defined", __func__, NULL); | |
| 2186 if (size < 13) { | |
| 2187 L_WARNING("size < 13; setting to 13\n", __func__); | |
| 2188 size = 13; | |
| 2189 } | |
| 2190 if (size % 2 == 0) | |
| 2191 size++; | |
| 2192 if (gthick < 2) { | |
| 2193 L_WARNING("grid thickness < 2; setting to 2\n", __func__); | |
| 2194 gthick = 2; | |
| 2195 } | |
| 2196 selGetParameters(sel, &sy, &sx, &cy, &cx); | |
| 2197 w = size * sx + gthick * (sx + 1); | |
| 2198 h = size * sy + gthick * (sy + 1); | |
| 2199 pixd = pixCreate(w, h, 1); | |
| 2200 | |
| 2201 /* Generate grid lines */ | |
| 2202 for (i = 0; i <= sy; i++) | |
| 2203 pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), | |
| 2204 w - 1, gthick / 2 + i * (size + gthick), | |
| 2205 gthick, L_SET_PIXELS); | |
| 2206 for (j = 0; j <= sx; j++) | |
| 2207 pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, | |
| 2208 gthick / 2 + j * (size + gthick), h - 1, | |
| 2209 gthick, L_SET_PIXELS); | |
| 2210 | |
| 2211 /* Generate hit and miss patterns */ | |
| 2212 radius1 = (l_int32)(0.85 * ((size - 1) / 2.0) + 0.5); /* of hit */ | |
| 2213 radius2 = (l_int32)(0.65 * ((size - 1) / 2.0) + 0.5); /* of inner miss */ | |
| 2214 pta1 = generatePtaFilledCircle(radius1); | |
| 2215 pta2 = generatePtaFilledCircle(radius2); | |
| 2216 shift1 = (size - 1) / 2 - radius1; /* center circle in square */ | |
| 2217 shift2 = (size - 1) / 2 - radius2; | |
| 2218 pta1t = ptaTransform(pta1, shift1, shift1, 1.0, 1.0); | |
| 2219 pta2t = ptaTransform(pta2, shift2, shift2, 1.0, 1.0); | |
| 2220 pixh = pixGenerateFromPta(pta1t, size, size); /* hits */ | |
| 2221 pix2 = pixGenerateFromPta(pta2t, size, size); | |
| 2222 pixm = pixSubtract(NULL, pixh, pix2); | |
| 2223 | |
| 2224 /* Generate crossed lines for origin pattern */ | |
| 2225 pixorig = pixCreate(size, size, 1); | |
| 2226 width = size / 8; | |
| 2227 pixRenderLine(pixorig, size / 2, (l_int32)(0.12 * size), | |
| 2228 size / 2, (l_int32)(0.88 * size), | |
| 2229 width, L_SET_PIXELS); | |
| 2230 pixRenderLine(pixorig, (l_int32)(0.15 * size), size / 2, | |
| 2231 (l_int32)(0.85 * size), size / 2, | |
| 2232 width, L_FLIP_PIXELS); | |
| 2233 pixRasterop(pixorig, size / 2 - width, size / 2 - width, | |
| 2234 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); | |
| 2235 | |
| 2236 /* Specialize origin pattern for this sel */ | |
| 2237 selGetTypeAtOrigin(sel, &type); | |
| 2238 if (type == SEL_HIT) | |
| 2239 pixXor(pixorig, pixorig, pixh); | |
| 2240 else if (type == SEL_MISS) | |
| 2241 pixXor(pixorig, pixorig, pixm); | |
| 2242 | |
| 2243 /* Paste the patterns in */ | |
| 2244 y0 = gthick; | |
| 2245 for (i = 0; i < sy; i++) { | |
| 2246 x0 = gthick; | |
| 2247 for (j = 0; j < sx; j++) { | |
| 2248 selGetElement(sel, i, j, &type); | |
| 2249 if (i == cy && j == cx) /* origin */ | |
| 2250 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixorig, 0, 0); | |
| 2251 else if (type == SEL_HIT) | |
| 2252 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixh, 0, 0); | |
| 2253 else if (type == SEL_MISS) | |
| 2254 pixRasterop(pixd, x0, y0, size, size, PIX_SRC, pixm, 0, 0); | |
| 2255 x0 += size + gthick; | |
| 2256 } | |
| 2257 y0 += size + gthick; | |
| 2258 } | |
| 2259 | |
| 2260 pixDestroy(&pix2); | |
| 2261 pixDestroy(&pixh); | |
| 2262 pixDestroy(&pixm); | |
| 2263 pixDestroy(&pixorig); | |
| 2264 ptaDestroy(&pta1); | |
| 2265 ptaDestroy(&pta1t); | |
| 2266 ptaDestroy(&pta2); | |
| 2267 ptaDestroy(&pta2t); | |
| 2268 return pixd; | |
| 2269 } | |
| 2270 | |
| 2271 | |
| 2272 /*! | |
| 2273 * \brief selaDisplayInPix() | |
| 2274 * | |
| 2275 * \param[in] sela | |
| 2276 * \param[in] size of grid interiors; odd; minimum size of 13 is enforced | |
| 2277 * \param[in] gthick grid thickness; minimum size of 2 is enforced | |
| 2278 * \param[in] spacing between sels, both horizontally and vertically | |
| 2279 * \param[in] ncols number of sels per "line" | |
| 2280 * \return pix display of all sels in sela, or NULL on error | |
| 2281 * | |
| 2282 * <pre> | |
| 2283 * Notes: | |
| 2284 * (1) This gives a visual representation of all the sels in a sela. | |
| 2285 * (2) See notes in selDisplayInPix() for display params of each sel. | |
| 2286 * (3) This gives the nicest results when all sels in the sela | |
| 2287 * are the same size. | |
| 2288 * </pre> | |
| 2289 */ | |
| 2290 PIX * | |
| 2291 selaDisplayInPix(SELA *sela, | |
| 2292 l_int32 size, | |
| 2293 l_int32 gthick, | |
| 2294 l_int32 spacing, | |
| 2295 l_int32 ncols) | |
| 2296 { | |
| 2297 l_int32 nsels, i, w, width; | |
| 2298 PIX *pixt, *pixd; | |
| 2299 PIXA *pixa; | |
| 2300 SEL *sel; | |
| 2301 | |
| 2302 if (!sela) | |
| 2303 return (PIX *)ERROR_PTR("sela not defined", __func__, NULL); | |
| 2304 if (size < 13) { | |
| 2305 L_WARNING("size < 13; setting to 13\n", __func__); | |
| 2306 size = 13; | |
| 2307 } | |
| 2308 if (size % 2 == 0) | |
| 2309 size++; | |
| 2310 if (gthick < 2) { | |
| 2311 L_WARNING("grid thickness < 2; setting to 2\n", __func__); | |
| 2312 gthick = 2; | |
| 2313 } | |
| 2314 if (spacing < 5) { | |
| 2315 L_WARNING("spacing < 5; setting to 5\n", __func__); | |
| 2316 spacing = 5; | |
| 2317 } | |
| 2318 | |
| 2319 /* Accumulate the pix of each sel */ | |
| 2320 nsels = selaGetCount(sela); | |
| 2321 pixa = pixaCreate(nsels); | |
| 2322 for (i = 0; i < nsels; i++) { | |
| 2323 sel = selaGetSel(sela, i); | |
| 2324 pixt = selDisplayInPix(sel, size, gthick); | |
| 2325 pixaAddPix(pixa, pixt, L_INSERT); | |
| 2326 } | |
| 2327 | |
| 2328 /* Find the tiled output width, using just the first | |
| 2329 * ncols pix in the pixa. If all pix have the same width, | |
| 2330 * they will align properly in columns. */ | |
| 2331 width = 0; | |
| 2332 ncols = L_MIN(nsels, ncols); | |
| 2333 for (i = 0; i < ncols; i++) { | |
| 2334 pixt = pixaGetPix(pixa, i, L_CLONE); | |
| 2335 pixGetDimensions(pixt, &w, NULL, NULL); | |
| 2336 width += w; | |
| 2337 pixDestroy(&pixt); | |
| 2338 } | |
| 2339 width += (ncols + 1) * spacing; /* add spacing all around as well */ | |
| 2340 | |
| 2341 pixd = pixaDisplayTiledInRows(pixa, 1, width, 1.0, 0, spacing, 0); | |
| 2342 pixaDestroy(&pixa); | |
| 2343 return pixd; | |
| 2344 } |
