Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/ptafunc1.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 * \file ptafunc1.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * -------------------------------------- | |
| 32 * This file has these Pta utilities: | |
| 33 * - simple rearrangements | |
| 34 * - geometric analysis | |
| 35 * - min/max and filtering | |
| 36 * - least squares fitting | |
| 37 * - interconversions with Pix and Numa | |
| 38 * - display into a pix | |
| 39 * -------------------------------------- | |
| 40 * | |
| 41 * Simple rearrangements | |
| 42 * PTA *ptaSubsample() | |
| 43 * l_int32 ptaJoin() | |
| 44 * l_int32 ptaaJoin() | |
| 45 * PTA *ptaReverse() | |
| 46 * PTA *ptaTranspose() | |
| 47 * PTA *ptaCyclicPerm() | |
| 48 * PTA *ptaSelectRange() | |
| 49 * | |
| 50 * Geometric | |
| 51 * BOX *ptaGetBoundingRegion() | |
| 52 * l_int32 *ptaGetRange() | |
| 53 * PTA *ptaGetInsideBox() | |
| 54 * PTA *pixFindCornerPixels() | |
| 55 * l_int32 ptaContainsPt() | |
| 56 * l_int32 ptaTestIntersection() | |
| 57 * PTA *ptaTransform() | |
| 58 * l_int32 ptaPtInsidePolygon() | |
| 59 * l_float32 l_angleBetweenVectors() | |
| 60 * l_int32 ptaPolygonIsConvex() | |
| 61 * | |
| 62 * Min/max and filtering | |
| 63 * l_int32 ptaGetMinMax() | |
| 64 * PTA *ptaSelectByValue() | |
| 65 * PTA *ptaCropToMask() | |
| 66 * | |
| 67 * Least Squares Fit | |
| 68 * l_int32 ptaGetLinearLSF() | |
| 69 * l_int32 ptaGetQuadraticLSF() | |
| 70 * l_int32 ptaGetCubicLSF() | |
| 71 * l_int32 ptaGetQuarticLSF() | |
| 72 * l_int32 ptaNoisyLinearLSF() | |
| 73 * l_int32 ptaNoisyQuadraticLSF() | |
| 74 * l_int32 applyLinearFit() | |
| 75 * l_int32 applyQuadraticFit() | |
| 76 * l_int32 applyCubicFit() | |
| 77 * l_int32 applyQuarticFit() | |
| 78 * | |
| 79 * Interconversions with Pix | |
| 80 * l_int32 pixPlotAlongPta() | |
| 81 * PTA *ptaGetPixelsFromPix() | |
| 82 * PIX *pixGenerateFromPta() | |
| 83 * PTA *ptaGetBoundaryPixels() | |
| 84 * PTAA *ptaaGetBoundaryPixels() | |
| 85 * PTAA *ptaaIndexLabeledPixels() | |
| 86 * PTA *ptaGetNeighborPixLocs() | |
| 87 * | |
| 88 * Interconversion with Numa | |
| 89 * PTA *numaConvertToPta1() | |
| 90 * PTA *numaConvertToPta2() | |
| 91 * l_int32 ptaConvertToNuma() | |
| 92 * | |
| 93 * Display Pta and Ptaa | |
| 94 * PIX *pixDisplayPta() | |
| 95 * PIX *pixDisplayPtaaPattern() | |
| 96 * PIX *pixDisplayPtaPattern() | |
| 97 * PTA *ptaReplicatePattern() | |
| 98 * PIX *pixDisplayPtaa() | |
| 99 * </pre> | |
| 100 */ | |
| 101 | |
| 102 #ifdef HAVE_CONFIG_H | |
| 103 #include <config_auto.h> | |
| 104 #endif /* HAVE_CONFIG_H */ | |
| 105 | |
| 106 #include <math.h> | |
| 107 #include "allheaders.h" | |
| 108 #include "pix_internal.h" | |
| 109 | |
| 110 #ifndef M_PI | |
| 111 #define M_PI 3.14159265358979323846 | |
| 112 #endif /* M_PI */ | |
| 113 | |
| 114 /*---------------------------------------------------------------------* | |
| 115 * Simple rearrangements * | |
| 116 *---------------------------------------------------------------------*/ | |
| 117 /*! | |
| 118 * \brief ptaSubsample() | |
| 119 * | |
| 120 * \param[in] ptas | |
| 121 * \param[in] subfactor subsample factor, >= 1 | |
| 122 * \return ptad evenly sampled pt values from ptas, or NULL on error | |
| 123 */ | |
| 124 PTA * | |
| 125 ptaSubsample(PTA *ptas, | |
| 126 l_int32 subfactor) | |
| 127 { | |
| 128 l_int32 n, i; | |
| 129 l_float32 x, y; | |
| 130 PTA *ptad; | |
| 131 | |
| 132 if (!ptas) | |
| 133 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 134 if (subfactor < 1) | |
| 135 return (PTA *)ERROR_PTR("subfactor < 1", __func__, NULL); | |
| 136 | |
| 137 ptad = ptaCreate(0); | |
| 138 n = ptaGetCount(ptas); | |
| 139 for (i = 0; i < n; i++) { | |
| 140 if (i % subfactor != 0) continue; | |
| 141 ptaGetPt(ptas, i, &x, &y); | |
| 142 ptaAddPt(ptad, x, y); | |
| 143 } | |
| 144 | |
| 145 return ptad; | |
| 146 } | |
| 147 | |
| 148 | |
| 149 /*! | |
| 150 * \brief ptaJoin() | |
| 151 * | |
| 152 * \param[in] ptad dest pta; add to this one | |
| 153 * \param[in] ptas source pta; add from this one | |
| 154 * \param[in] istart starting index in ptas | |
| 155 * \param[in] iend ending index in ptas; use -1 to cat all | |
| 156 * \return 0 if OK, 1 on error | |
| 157 * | |
| 158 * <pre> | |
| 159 * Notes: | |
| 160 * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) | |
| 161 * (2) iend < 0 means 'read to the end' | |
| 162 * (3) if ptas == NULL, this is a no-op | |
| 163 * </pre> | |
| 164 */ | |
| 165 l_ok | |
| 166 ptaJoin(PTA *ptad, | |
| 167 PTA *ptas, | |
| 168 l_int32 istart, | |
| 169 l_int32 iend) | |
| 170 { | |
| 171 l_int32 n, i, x, y; | |
| 172 | |
| 173 if (!ptad) | |
| 174 return ERROR_INT("ptad not defined", __func__, 1); | |
| 175 if (!ptas) | |
| 176 return 0; | |
| 177 | |
| 178 if (istart < 0) | |
| 179 istart = 0; | |
| 180 n = ptaGetCount(ptas); | |
| 181 if (iend < 0 || iend >= n) | |
| 182 iend = n - 1; | |
| 183 if (istart > iend) | |
| 184 return ERROR_INT("istart > iend; no pts", __func__, 1); | |
| 185 | |
| 186 for (i = istart; i <= iend; i++) { | |
| 187 ptaGetIPt(ptas, i, &x, &y); | |
| 188 if (ptaAddPt(ptad, x, y) == 1) { | |
| 189 L_ERROR("failed to add pt at i = %d\n", __func__, i); | |
| 190 return 1; | |
| 191 } | |
| 192 } | |
| 193 return 0; | |
| 194 } | |
| 195 | |
| 196 | |
| 197 /*! | |
| 198 * \brief ptaaJoin() | |
| 199 * | |
| 200 * \param[in] ptaad dest ptaa; add to this one | |
| 201 * \param[in] ptaas source ptaa; add from this one | |
| 202 * \param[in] istart starting index in ptaas | |
| 203 * \param[in] iend ending index in ptaas; use -1 to cat all | |
| 204 * \return 0 if OK, 1 on error | |
| 205 * | |
| 206 * <pre> | |
| 207 * Notes: | |
| 208 * (1) istart < 0 is taken to mean 'read from the start' (istart = 0) | |
| 209 * (2) iend < 0 means 'read to the end' | |
| 210 * (3) if ptas == NULL, this is a no-op | |
| 211 * </pre> | |
| 212 */ | |
| 213 l_ok | |
| 214 ptaaJoin(PTAA *ptaad, | |
| 215 PTAA *ptaas, | |
| 216 l_int32 istart, | |
| 217 l_int32 iend) | |
| 218 { | |
| 219 l_int32 n, i; | |
| 220 PTA *pta; | |
| 221 | |
| 222 if (!ptaad) | |
| 223 return ERROR_INT("ptaad not defined", __func__, 1); | |
| 224 if (!ptaas) | |
| 225 return 0; | |
| 226 | |
| 227 if (istart < 0) | |
| 228 istart = 0; | |
| 229 n = ptaaGetCount(ptaas); | |
| 230 if (iend < 0 || iend >= n) | |
| 231 iend = n - 1; | |
| 232 if (istart > iend) | |
| 233 return ERROR_INT("istart > iend; no pts", __func__, 1); | |
| 234 | |
| 235 for (i = istart; i <= iend; i++) { | |
| 236 pta = ptaaGetPta(ptaas, i, L_CLONE); | |
| 237 ptaaAddPta(ptaad, pta, L_INSERT); | |
| 238 } | |
| 239 | |
| 240 return 0; | |
| 241 } | |
| 242 | |
| 243 | |
| 244 /*! | |
| 245 * \brief ptaReverse() | |
| 246 * | |
| 247 * \param[in] ptas | |
| 248 * \param[in] type 0 for float values; 1 for integer values | |
| 249 * \return ptad reversed pta, or NULL on error | |
| 250 */ | |
| 251 PTA * | |
| 252 ptaReverse(PTA *ptas, | |
| 253 l_int32 type) | |
| 254 { | |
| 255 l_int32 n, i, ix, iy; | |
| 256 l_float32 x, y; | |
| 257 PTA *ptad; | |
| 258 | |
| 259 if (!ptas) | |
| 260 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 261 | |
| 262 n = ptaGetCount(ptas); | |
| 263 if ((ptad = ptaCreate(n)) == NULL) | |
| 264 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); | |
| 265 for (i = n - 1; i >= 0; i--) { | |
| 266 if (type == 0) { | |
| 267 ptaGetPt(ptas, i, &x, &y); | |
| 268 ptaAddPt(ptad, x, y); | |
| 269 } else { /* type == 1 */ | |
| 270 ptaGetIPt(ptas, i, &ix, &iy); | |
| 271 ptaAddPt(ptad, ix, iy); | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 return ptad; | |
| 276 } | |
| 277 | |
| 278 | |
| 279 /*! | |
| 280 * \brief ptaTranspose() | |
| 281 * | |
| 282 * \param[in] ptas | |
| 283 * \return ptad with x and y values swapped, or NULL on error | |
| 284 */ | |
| 285 PTA * | |
| 286 ptaTranspose(PTA *ptas) | |
| 287 { | |
| 288 l_int32 n, i; | |
| 289 l_float32 x, y; | |
| 290 PTA *ptad; | |
| 291 | |
| 292 if (!ptas) | |
| 293 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 294 | |
| 295 n = ptaGetCount(ptas); | |
| 296 if ((ptad = ptaCreate(n)) == NULL) | |
| 297 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); | |
| 298 for (i = 0; i < n; i++) { | |
| 299 ptaGetPt(ptas, i, &x, &y); | |
| 300 ptaAddPt(ptad, y, x); | |
| 301 } | |
| 302 | |
| 303 return ptad; | |
| 304 } | |
| 305 | |
| 306 | |
| 307 /*! | |
| 308 * \brief ptaCyclicPerm() | |
| 309 * | |
| 310 * \param[in] ptas | |
| 311 * \param[in] xs, ys start point; must be in ptas | |
| 312 * \return ptad cyclic permutation, starting and ending at (xs, ys, | |
| 313 * or NULL on error | |
| 314 * | |
| 315 * <pre> | |
| 316 * Notes: | |
| 317 * (1) Check to insure that (a) ptas is a closed path where | |
| 318 * the first and last points are identical, and (b) the | |
| 319 * resulting pta also starts and ends on the same point | |
| 320 * (which in this case is (xs, ys). | |
| 321 * </pre> | |
| 322 */ | |
| 323 PTA * | |
| 324 ptaCyclicPerm(PTA *ptas, | |
| 325 l_int32 xs, | |
| 326 l_int32 ys) | |
| 327 { | |
| 328 l_int32 n, i, x, y, j, index, state; | |
| 329 l_int32 x1, y1, x2, y2; | |
| 330 PTA *ptad; | |
| 331 | |
| 332 if (!ptas) | |
| 333 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 334 | |
| 335 n = ptaGetCount(ptas); | |
| 336 | |
| 337 /* Verify input data */ | |
| 338 ptaGetIPt(ptas, 0, &x1, &y1); | |
| 339 ptaGetIPt(ptas, n - 1, &x2, &y2); | |
| 340 if (x1 != x2 || y1 != y2) | |
| 341 return (PTA *)ERROR_PTR("start and end pts not same", __func__, NULL); | |
| 342 state = L_NOT_FOUND; | |
| 343 for (i = 0; i < n; i++) { | |
| 344 ptaGetIPt(ptas, i, &x, &y); | |
| 345 if (x == xs && y == ys) { | |
| 346 state = L_FOUND; | |
| 347 break; | |
| 348 } | |
| 349 } | |
| 350 if (state == L_NOT_FOUND) | |
| 351 return (PTA *)ERROR_PTR("start pt not in ptas", __func__, NULL); | |
| 352 | |
| 353 if ((ptad = ptaCreate(n)) == NULL) | |
| 354 return (PTA *)ERROR_PTR("ptad not made", __func__, NULL); | |
| 355 for (j = 0; j < n - 1; j++) { | |
| 356 if (i + j < n - 1) | |
| 357 index = i + j; | |
| 358 else | |
| 359 index = (i + j + 1) % n; | |
| 360 ptaGetIPt(ptas, index, &x, &y); | |
| 361 ptaAddPt(ptad, x, y); | |
| 362 } | |
| 363 ptaAddPt(ptad, xs, ys); | |
| 364 | |
| 365 return ptad; | |
| 366 } | |
| 367 | |
| 368 | |
| 369 /*! | |
| 370 * \brief ptaSelectRange() | |
| 371 * | |
| 372 * \param[in] ptas | |
| 373 * \param[in] first use 0 to select from the beginning | |
| 374 * \param[in] last use -1 to select to the end | |
| 375 * \return ptad, or NULL on error | |
| 376 */ | |
| 377 PTA * | |
| 378 ptaSelectRange(PTA *ptas, | |
| 379 l_int32 first, | |
| 380 l_int32 last) | |
| 381 { | |
| 382 l_int32 n, npt, i; | |
| 383 l_float32 x, y; | |
| 384 PTA *ptad; | |
| 385 | |
| 386 if (!ptas) | |
| 387 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 388 if ((n = ptaGetCount(ptas)) == 0) { | |
| 389 L_WARNING("ptas is empty\n", __func__); | |
| 390 return ptaCopy(ptas); | |
| 391 } | |
| 392 first = L_MAX(0, first); | |
| 393 if (last < 0) last = n - 1; | |
| 394 if (first >= n) | |
| 395 return (PTA *)ERROR_PTR("invalid first", __func__, NULL); | |
| 396 if (last >= n) { | |
| 397 L_WARNING("last = %d is beyond max index = %d; adjusting\n", | |
| 398 __func__, last, n - 1); | |
| 399 last = n - 1; | |
| 400 } | |
| 401 if (first > last) | |
| 402 return (PTA *)ERROR_PTR("first > last", __func__, NULL); | |
| 403 | |
| 404 npt = last - first + 1; | |
| 405 ptad = ptaCreate(npt); | |
| 406 for (i = first; i <= last; i++) { | |
| 407 ptaGetPt(ptas, i, &x, &y); | |
| 408 ptaAddPt(ptad, x, y); | |
| 409 } | |
| 410 return ptad; | |
| 411 } | |
| 412 | |
| 413 | |
| 414 /*---------------------------------------------------------------------* | |
| 415 * Geometric * | |
| 416 *---------------------------------------------------------------------*/ | |
| 417 /*! | |
| 418 * \brief ptaGetBoundingRegion() | |
| 419 * | |
| 420 * \param[in] pta | |
| 421 * \return box, or NULL on error | |
| 422 * | |
| 423 * <pre> | |
| 424 * Notes: | |
| 425 * (1) This is used when the pta represents a set of points in | |
| 426 * a two-dimensional image. It returns the box of minimum | |
| 427 * size containing the pts in the pta. | |
| 428 * </pre> | |
| 429 */ | |
| 430 BOX * | |
| 431 ptaGetBoundingRegion(PTA *pta) | |
| 432 { | |
| 433 l_int32 n, i, x, y, minx, maxx, miny, maxy; | |
| 434 | |
| 435 if (!pta) | |
| 436 return (BOX *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 437 | |
| 438 minx = 10000000; | |
| 439 miny = 10000000; | |
| 440 maxx = -10000000; | |
| 441 maxy = -10000000; | |
| 442 n = ptaGetCount(pta); | |
| 443 for (i = 0; i < n; i++) { | |
| 444 ptaGetIPt(pta, i, &x, &y); | |
| 445 if (x < minx) minx = x; | |
| 446 if (x > maxx) maxx = x; | |
| 447 if (y < miny) miny = y; | |
| 448 if (y > maxy) maxy = y; | |
| 449 } | |
| 450 | |
| 451 return boxCreate(minx, miny, maxx - minx + 1, maxy - miny + 1); | |
| 452 } | |
| 453 | |
| 454 | |
| 455 /*! | |
| 456 * \brief ptaGetRange() | |
| 457 * | |
| 458 * \param[in] pta | |
| 459 * \param[out] pminx [optional] min value of x | |
| 460 * \param[out] pmaxx [optional] max value of x | |
| 461 * \param[out] pminy [optional] min value of y | |
| 462 * \param[out] pmaxy [optional] max value of y | |
| 463 * \return 0 if OK, 1 on error | |
| 464 * | |
| 465 * <pre> | |
| 466 * Notes: | |
| 467 * (1) We can use pts to represent pairs of floating values, that | |
| 468 * are not necessarily tied to a two-dimension region. For | |
| 469 * example, the pts can represent a general function y(x). | |
| 470 * </pre> | |
| 471 */ | |
| 472 l_ok | |
| 473 ptaGetRange(PTA *pta, | |
| 474 l_float32 *pminx, | |
| 475 l_float32 *pmaxx, | |
| 476 l_float32 *pminy, | |
| 477 l_float32 *pmaxy) | |
| 478 { | |
| 479 l_int32 n, i; | |
| 480 l_float32 x, y, minx, maxx, miny, maxy; | |
| 481 | |
| 482 if (!pminx && !pmaxx && !pminy && !pmaxy) | |
| 483 return ERROR_INT("no output requested", __func__, 1); | |
| 484 if (pminx) *pminx = 0; | |
| 485 if (pmaxx) *pmaxx = 0; | |
| 486 if (pminy) *pminy = 0; | |
| 487 if (pmaxy) *pmaxy = 0; | |
| 488 if (!pta) | |
| 489 return ERROR_INT("pta not defined", __func__, 1); | |
| 490 if ((n = ptaGetCount(pta)) == 0) | |
| 491 return ERROR_INT("no points in pta", __func__, 1); | |
| 492 | |
| 493 ptaGetPt(pta, 0, &x, &y); | |
| 494 minx = x; | |
| 495 maxx = x; | |
| 496 miny = y; | |
| 497 maxy = y; | |
| 498 for (i = 1; i < n; i++) { | |
| 499 ptaGetPt(pta, i, &x, &y); | |
| 500 if (x < minx) minx = x; | |
| 501 if (x > maxx) maxx = x; | |
| 502 if (y < miny) miny = y; | |
| 503 if (y > maxy) maxy = y; | |
| 504 } | |
| 505 if (pminx) *pminx = minx; | |
| 506 if (pmaxx) *pmaxx = maxx; | |
| 507 if (pminy) *pminy = miny; | |
| 508 if (pmaxy) *pmaxy = maxy; | |
| 509 return 0; | |
| 510 } | |
| 511 | |
| 512 | |
| 513 /*! | |
| 514 * \brief ptaGetInsideBox() | |
| 515 * | |
| 516 * \param[in] ptas input pts | |
| 517 * \param[in] box | |
| 518 * \return ptad of pts in ptas that are inside the box, or NULL on error | |
| 519 */ | |
| 520 PTA * | |
| 521 ptaGetInsideBox(PTA *ptas, | |
| 522 BOX *box) | |
| 523 { | |
| 524 PTA *ptad; | |
| 525 l_int32 n, i, contains; | |
| 526 l_float32 x, y; | |
| 527 | |
| 528 if (!ptas) | |
| 529 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 530 if (!box) | |
| 531 return (PTA *)ERROR_PTR("box not defined", __func__, NULL); | |
| 532 | |
| 533 n = ptaGetCount(ptas); | |
| 534 ptad = ptaCreate(0); | |
| 535 for (i = 0; i < n; i++) { | |
| 536 ptaGetPt(ptas, i, &x, &y); | |
| 537 boxContainsPt(box, x, y, &contains); | |
| 538 if (contains) | |
| 539 ptaAddPt(ptad, x, y); | |
| 540 } | |
| 541 | |
| 542 return ptad; | |
| 543 } | |
| 544 | |
| 545 | |
| 546 /*! | |
| 547 * \brief pixFindCornerPixels() | |
| 548 * | |
| 549 * \param[in] pixs 1 bpp | |
| 550 * \return pta, or NULL on error | |
| 551 * | |
| 552 * <pre> | |
| 553 * Notes: | |
| 554 * (1) Finds the 4 corner-most pixels, as defined by a search | |
| 555 * inward from each corner, using a 45 degree line. | |
| 556 * </pre> | |
| 557 */ | |
| 558 PTA * | |
| 559 pixFindCornerPixels(PIX *pixs) | |
| 560 { | |
| 561 l_int32 i, j, x, y, w, h, wpl, mindim, found; | |
| 562 l_uint32 *data, *line; | |
| 563 PTA *pta; | |
| 564 | |
| 565 if (!pixs) | |
| 566 return (PTA *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 567 if (pixGetDepth(pixs) != 1) | |
| 568 return (PTA *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 569 | |
| 570 w = pixGetWidth(pixs); | |
| 571 h = pixGetHeight(pixs); | |
| 572 mindim = L_MIN(w, h); | |
| 573 data = pixGetData(pixs); | |
| 574 wpl = pixGetWpl(pixs); | |
| 575 | |
| 576 if ((pta = ptaCreate(4)) == NULL) | |
| 577 return (PTA *)ERROR_PTR("pta not made", __func__, NULL); | |
| 578 | |
| 579 for (found = FALSE, i = 0; i < mindim; i++) { | |
| 580 for (j = 0; j <= i; j++) { | |
| 581 y = i - j; | |
| 582 line = data + y * wpl; | |
| 583 if (GET_DATA_BIT(line, j)) { | |
| 584 ptaAddPt(pta, j, y); | |
| 585 found = TRUE; | |
| 586 break; | |
| 587 } | |
| 588 } | |
| 589 if (found == TRUE) | |
| 590 break; | |
| 591 } | |
| 592 | |
| 593 for (found = FALSE, i = 0; i < mindim; i++) { | |
| 594 for (j = 0; j <= i; j++) { | |
| 595 y = i - j; | |
| 596 line = data + y * wpl; | |
| 597 x = w - 1 - j; | |
| 598 if (GET_DATA_BIT(line, x)) { | |
| 599 ptaAddPt(pta, x, y); | |
| 600 found = TRUE; | |
| 601 break; | |
| 602 } | |
| 603 } | |
| 604 if (found == TRUE) | |
| 605 break; | |
| 606 } | |
| 607 | |
| 608 for (found = FALSE, i = 0; i < mindim; i++) { | |
| 609 for (j = 0; j <= i; j++) { | |
| 610 y = h - 1 - i + j; | |
| 611 line = data + y * wpl; | |
| 612 if (GET_DATA_BIT(line, j)) { | |
| 613 ptaAddPt(pta, j, y); | |
| 614 found = TRUE; | |
| 615 break; | |
| 616 } | |
| 617 } | |
| 618 if (found == TRUE) | |
| 619 break; | |
| 620 } | |
| 621 | |
| 622 for (found = FALSE, i = 0; i < mindim; i++) { | |
| 623 for (j = 0; j <= i; j++) { | |
| 624 y = h - 1 - i + j; | |
| 625 line = data + y * wpl; | |
| 626 x = w - 1 - j; | |
| 627 if (GET_DATA_BIT(line, x)) { | |
| 628 ptaAddPt(pta, x, y); | |
| 629 found = TRUE; | |
| 630 break; | |
| 631 } | |
| 632 } | |
| 633 if (found == TRUE) | |
| 634 break; | |
| 635 } | |
| 636 | |
| 637 return pta; | |
| 638 } | |
| 639 | |
| 640 | |
| 641 /*! | |
| 642 * \brief ptaContainsPt() | |
| 643 * | |
| 644 * \param[in] pta | |
| 645 * \param[in] x, y point | |
| 646 * \return 1 if contained, 0 otherwise or on error | |
| 647 */ | |
| 648 l_int32 | |
| 649 ptaContainsPt(PTA *pta, | |
| 650 l_int32 x, | |
| 651 l_int32 y) | |
| 652 { | |
| 653 l_int32 i, n, ix, iy; | |
| 654 | |
| 655 if (!pta) | |
| 656 return ERROR_INT("pta not defined", __func__, 0); | |
| 657 | |
| 658 n = ptaGetCount(pta); | |
| 659 for (i = 0; i < n; i++) { | |
| 660 ptaGetIPt(pta, i, &ix, &iy); | |
| 661 if (x == ix && y == iy) | |
| 662 return 1; | |
| 663 } | |
| 664 return 0; | |
| 665 } | |
| 666 | |
| 667 | |
| 668 /*! | |
| 669 * \brief ptaTestIntersection() | |
| 670 * | |
| 671 * \param[in] pta1, pta2 | |
| 672 * \return bval which is 1 if they have any elements in common; | |
| 673 * 0 otherwise or on error. | |
| 674 */ | |
| 675 l_int32 | |
| 676 ptaTestIntersection(PTA *pta1, | |
| 677 PTA *pta2) | |
| 678 { | |
| 679 l_int32 i, j, n1, n2, x1, y1, x2, y2; | |
| 680 | |
| 681 if (!pta1) | |
| 682 return ERROR_INT("pta1 not defined", __func__, 0); | |
| 683 if (!pta2) | |
| 684 return ERROR_INT("pta2 not defined", __func__, 0); | |
| 685 | |
| 686 n1 = ptaGetCount(pta1); | |
| 687 n2 = ptaGetCount(pta2); | |
| 688 for (i = 0; i < n1; i++) { | |
| 689 ptaGetIPt(pta1, i, &x1, &y1); | |
| 690 for (j = 0; j < n2; j++) { | |
| 691 ptaGetIPt(pta2, i, &x2, &y2); | |
| 692 if (x1 == x2 && y1 == y2) | |
| 693 return 1; | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 return 0; | |
| 698 } | |
| 699 | |
| 700 | |
| 701 /*! | |
| 702 * \brief ptaTransform() | |
| 703 * | |
| 704 * \param[in] ptas | |
| 705 * \param[in] shiftx, shifty | |
| 706 * \param[in] scalex, scaley | |
| 707 * \return pta, or NULL on error | |
| 708 * | |
| 709 * <pre> | |
| 710 * Notes: | |
| 711 * (1) Shift first, then scale. | |
| 712 * </pre> | |
| 713 */ | |
| 714 PTA * | |
| 715 ptaTransform(PTA *ptas, | |
| 716 l_int32 shiftx, | |
| 717 l_int32 shifty, | |
| 718 l_float32 scalex, | |
| 719 l_float32 scaley) | |
| 720 { | |
| 721 l_int32 n, i, x, y; | |
| 722 PTA *ptad; | |
| 723 | |
| 724 if (!ptas) | |
| 725 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 726 n = ptaGetCount(ptas); | |
| 727 ptad = ptaCreate(n); | |
| 728 for (i = 0; i < n; i++) { | |
| 729 ptaGetIPt(ptas, i, &x, &y); | |
| 730 x = (l_int32)(scalex * (x + shiftx) + 0.5); | |
| 731 y = (l_int32)(scaley * (y + shifty) + 0.5); | |
| 732 ptaAddPt(ptad, x, y); | |
| 733 } | |
| 734 | |
| 735 return ptad; | |
| 736 } | |
| 737 | |
| 738 | |
| 739 /*! | |
| 740 * \brief ptaPtInsidePolygon() | |
| 741 * | |
| 742 * \param[in] pta vertices of a polygon | |
| 743 * \param[in] x, y point to be tested | |
| 744 * \param[out] pinside 1 if inside; 0 if outside or on boundary | |
| 745 * \return 1 if OK, 0 on error | |
| 746 * | |
| 747 * The abs value of the sum of the angles subtended from a point by | |
| 748 * the sides of a polygon, when taken in order traversing the polygon, | |
| 749 * is 0 if the point is outside the polygon and 2*pi if inside. | |
| 750 * The sign will be positive if traversed cw and negative if ccw. | |
| 751 */ | |
| 752 l_int32 | |
| 753 ptaPtInsidePolygon(PTA *pta, | |
| 754 l_float32 x, | |
| 755 l_float32 y, | |
| 756 l_int32 *pinside) | |
| 757 { | |
| 758 l_int32 i, n; | |
| 759 l_float32 sum, x1, y1, x2, y2, xp1, yp1, xp2, yp2; | |
| 760 | |
| 761 if (!pinside) | |
| 762 return ERROR_INT("&inside not defined", __func__, 1); | |
| 763 *pinside = 0; | |
| 764 if (!pta) | |
| 765 return ERROR_INT("pta not defined", __func__, 1); | |
| 766 | |
| 767 /* Think of (x1,y1) as the end point of a vector that starts | |
| 768 * from the origin (0,0), and ditto for (x2,y2). */ | |
| 769 n = ptaGetCount(pta); | |
| 770 sum = 0.0; | |
| 771 for (i = 0; i < n; i++) { | |
| 772 ptaGetPt(pta, i, &xp1, &yp1); | |
| 773 ptaGetPt(pta, (i + 1) % n, &xp2, &yp2); | |
| 774 x1 = xp1 - x; | |
| 775 y1 = yp1 - y; | |
| 776 x2 = xp2 - x; | |
| 777 y2 = yp2 - y; | |
| 778 sum += l_angleBetweenVectors(x1, y1, x2, y2); | |
| 779 } | |
| 780 | |
| 781 if (L_ABS(sum) > M_PI) | |
| 782 *pinside = 1; | |
| 783 return 0; | |
| 784 } | |
| 785 | |
| 786 | |
| 787 /*! | |
| 788 * \brief l_angleBetweenVectors() | |
| 789 * | |
| 790 * \param[in] x1, y1 end point of first vector | |
| 791 * \param[in] x2, y2 end point of second vector | |
| 792 * \return angle radians, or 0.0 on error | |
| 793 * | |
| 794 * <pre> | |
| 795 * Notes: | |
| 796 * (1) This gives the angle between two vectors, going between | |
| 797 * vector1 (x1,y1) and vector2 (x2,y2). The angle is swept | |
| 798 * out from 1 --> 2. If this is clockwise, the angle is | |
| 799 * positive, but the result is folded into the interval [-pi, pi]. | |
| 800 * </pre> | |
| 801 */ | |
| 802 l_float32 | |
| 803 l_angleBetweenVectors(l_float32 x1, | |
| 804 l_float32 y1, | |
| 805 l_float32 x2, | |
| 806 l_float32 y2) | |
| 807 { | |
| 808 l_float64 ang; | |
| 809 | |
| 810 ang = atan2(y2, x2) - atan2(y1, x1); | |
| 811 if (ang > M_PI) ang -= 2.0 * M_PI; | |
| 812 if (ang < -M_PI) ang += 2.0 * M_PI; | |
| 813 return ang; | |
| 814 } | |
| 815 | |
| 816 | |
| 817 /*! | |
| 818 * \brief ptaPolygonIsConvex() | |
| 819 * | |
| 820 * \param[in] pta corners of polygon | |
| 821 * \param[out] pisconvex 1 if convex; 0 otherwise | |
| 822 * \return 0 if OK, 1 on error | |
| 823 * | |
| 824 * <pre> | |
| 825 * Notes: | |
| 826 * (1) A Pta of size n describes a polygon with n sides, where | |
| 827 * the n-th side goes from point[n - 1] to point[0]. | |
| 828 * (2) The pta must describe a CLOCKWISE traversal of the boundary | |
| 829 * of the polygon. | |
| 830 * (3) Algorithm: traversing the boundary in a cw direction, the | |
| 831 * polygon interior is always on the right. If the polygon is | |
| 832 * convex, for each set of 3 points, the third point is either | |
| 833 * on the ray extending from the first point and going through | |
| 834 * the second point, or to the right of it. | |
| 835 * </pre> | |
| 836 */ | |
| 837 l_int32 | |
| 838 ptaPolygonIsConvex(PTA *pta, | |
| 839 l_int32 *pisconvex) | |
| 840 { | |
| 841 l_int32 i, n; | |
| 842 l_float32 x0, y0, x1, y1, x2, y2; | |
| 843 l_float64 cprod; | |
| 844 | |
| 845 if (!pisconvex) | |
| 846 return ERROR_INT("&isconvex not defined", __func__, 1); | |
| 847 *pisconvex = 0; | |
| 848 if (!pta) | |
| 849 return ERROR_INT("pta not defined", __func__, 1); | |
| 850 if ((n = ptaGetCount(pta)) < 3) | |
| 851 return ERROR_INT("pta has < 3 pts", __func__, 1); | |
| 852 | |
| 853 for (i = 0; i < n; i++) { | |
| 854 ptaGetPt(pta, i, &x0, &y0); | |
| 855 ptaGetPt(pta, (i + 1) % n, &x1, &y1); | |
| 856 ptaGetPt(pta, (i + 2) % n, &x2, &y2); | |
| 857 /* The vector v02 from p0 to p2 must be to the right of the | |
| 858 vector v01 from p0 to p1. This is true if the cross | |
| 859 product v02 x v01 > 0. In coordinates: | |
| 860 v02x * v01y - v01x * v02y, where | |
| 861 v01x = x1 - x0, v01y = y1 - y0, | |
| 862 v02x = x2 - x0, v02y = y2 - y0 */ | |
| 863 cprod = (x2 - x0) * (y1 - y0) - (x1 - x0) * (y2 - y0); | |
| 864 if (cprod < -0.0001) /* small delta for float accuracy; test fails */ | |
| 865 return 0; | |
| 866 } | |
| 867 *pisconvex = 1; | |
| 868 return 0; | |
| 869 } | |
| 870 | |
| 871 | |
| 872 /*---------------------------------------------------------------------* | |
| 873 * Min/max and filtering * | |
| 874 *---------------------------------------------------------------------*/ | |
| 875 /*! | |
| 876 * \brief ptaGetMinMax() | |
| 877 * | |
| 878 * \param[in] pta | |
| 879 * \param[out] pxmin [optional] min of x | |
| 880 * \param[out] pymin [optional] min of y | |
| 881 * \param[out] pxmax [optional] max of x | |
| 882 * \param[out] pymax [optional] max of y | |
| 883 * \return 0 if OK, 1 on error. If pta is empty, requested | |
| 884 * values are returned as -1.0. | |
| 885 */ | |
| 886 l_ok | |
| 887 ptaGetMinMax(PTA *pta, | |
| 888 l_float32 *pxmin, | |
| 889 l_float32 *pymin, | |
| 890 l_float32 *pxmax, | |
| 891 l_float32 *pymax) | |
| 892 { | |
| 893 l_int32 i, n; | |
| 894 l_float32 x, y, xmin, ymin, xmax, ymax; | |
| 895 | |
| 896 if (pxmin) *pxmin = -1.0; | |
| 897 if (pymin) *pymin = -1.0; | |
| 898 if (pxmax) *pxmax = -1.0; | |
| 899 if (pymax) *pymax = -1.0; | |
| 900 if (!pta) | |
| 901 return ERROR_INT("pta not defined", __func__, 1); | |
| 902 if (!pxmin && !pxmax && !pymin && !pymax) | |
| 903 return ERROR_INT("no output requested", __func__, 1); | |
| 904 if ((n = ptaGetCount(pta)) == 0) { | |
| 905 L_WARNING("pta is empty\n", __func__); | |
| 906 return 0; | |
| 907 } | |
| 908 | |
| 909 xmin = ymin = 1.0e20f; | |
| 910 xmax = ymax = -1.0e20f; | |
| 911 for (i = 0; i < n; i++) { | |
| 912 ptaGetPt(pta, i, &x, &y); | |
| 913 if (x < xmin) xmin = x; | |
| 914 if (y < ymin) ymin = y; | |
| 915 if (x > xmax) xmax = x; | |
| 916 if (y > ymax) ymax = y; | |
| 917 } | |
| 918 if (pxmin) *pxmin = xmin; | |
| 919 if (pymin) *pymin = ymin; | |
| 920 if (pxmax) *pxmax = xmax; | |
| 921 if (pymax) *pymax = ymax; | |
| 922 return 0; | |
| 923 } | |
| 924 | |
| 925 | |
| 926 /*! | |
| 927 * \brief ptaSelectByValue() | |
| 928 * | |
| 929 * \param[in] ptas | |
| 930 * \param[in] xth, yth threshold values | |
| 931 * \param[in] type L_SELECT_XVAL, L_SELECT_YVAL, | |
| 932 * L_SELECT_IF_EITHER, L_SELECT_IF_BOTH | |
| 933 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT, | |
| 934 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 935 * \return ptad filtered set, or NULL on error | |
| 936 */ | |
| 937 PTA * | |
| 938 ptaSelectByValue(PTA *ptas, | |
| 939 l_float32 xth, | |
| 940 l_float32 yth, | |
| 941 l_int32 type, | |
| 942 l_int32 relation) | |
| 943 { | |
| 944 l_int32 i, n; | |
| 945 l_float32 x, y; | |
| 946 PTA *ptad; | |
| 947 | |
| 948 if (!ptas) | |
| 949 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 950 if (ptaGetCount(ptas) == 0) { | |
| 951 L_WARNING("ptas is empty\n", __func__); | |
| 952 return ptaCopy(ptas); | |
| 953 } | |
| 954 if (type != L_SELECT_XVAL && type != L_SELECT_YVAL && | |
| 955 type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH) | |
| 956 return (PTA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 957 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 958 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 959 return (PTA *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 960 | |
| 961 n = ptaGetCount(ptas); | |
| 962 ptad = ptaCreate(n); | |
| 963 for (i = 0; i < n; i++) { | |
| 964 ptaGetPt(ptas, i, &x, &y); | |
| 965 if (type == L_SELECT_XVAL) { | |
| 966 if ((relation == L_SELECT_IF_LT && x < xth) || | |
| 967 (relation == L_SELECT_IF_GT && x > xth) || | |
| 968 (relation == L_SELECT_IF_LTE && x <= xth) || | |
| 969 (relation == L_SELECT_IF_GTE && x >= xth)) | |
| 970 ptaAddPt(ptad, x, y); | |
| 971 } else if (type == L_SELECT_YVAL) { | |
| 972 if ((relation == L_SELECT_IF_LT && y < yth) || | |
| 973 (relation == L_SELECT_IF_GT && y > yth) || | |
| 974 (relation == L_SELECT_IF_LTE && y <= yth) || | |
| 975 (relation == L_SELECT_IF_GTE && y >= yth)) | |
| 976 ptaAddPt(ptad, x, y); | |
| 977 } else if (type == L_SELECT_IF_EITHER) { | |
| 978 if (((relation == L_SELECT_IF_LT) && (x < xth || y < yth)) || | |
| 979 ((relation == L_SELECT_IF_GT) && (x > xth || y > yth)) || | |
| 980 ((relation == L_SELECT_IF_LTE) && (x <= xth || y <= yth)) || | |
| 981 ((relation == L_SELECT_IF_GTE) && (x >= xth || y >= yth))) | |
| 982 ptaAddPt(ptad, x, y); | |
| 983 } else { /* L_SELECT_IF_BOTH */ | |
| 984 if (((relation == L_SELECT_IF_LT) && (x < xth && y < yth)) || | |
| 985 ((relation == L_SELECT_IF_GT) && (x > xth && y > yth)) || | |
| 986 ((relation == L_SELECT_IF_LTE) && (x <= xth && y <= yth)) || | |
| 987 ((relation == L_SELECT_IF_GTE) && (x >= xth && y >= yth))) | |
| 988 ptaAddPt(ptad, x, y); | |
| 989 } | |
| 990 } | |
| 991 | |
| 992 return ptad; | |
| 993 } | |
| 994 | |
| 995 | |
| 996 /*! | |
| 997 * \brief ptaCropToMask() | |
| 998 * | |
| 999 * \param[in] ptas input pta | |
| 1000 * \param[in] pixm 1 bpp mask | |
| 1001 * \return ptad with only pts under the mask fg, or NULL on error | |
| 1002 */ | |
| 1003 PTA * | |
| 1004 ptaCropToMask(PTA *ptas, | |
| 1005 PIX *pixm) | |
| 1006 { | |
| 1007 l_int32 i, n, x, y; | |
| 1008 l_uint32 val; | |
| 1009 PTA *ptad; | |
| 1010 | |
| 1011 if (!ptas) | |
| 1012 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 1013 if (!pixm || pixGetDepth(pixm) != 1) | |
| 1014 return (PTA *)ERROR_PTR("pixm undefined or not 1 bpp", __func__, NULL); | |
| 1015 if (ptaGetCount(ptas) == 0) { | |
| 1016 L_INFO("ptas is empty\n", __func__); | |
| 1017 return ptaCopy(ptas); | |
| 1018 } | |
| 1019 | |
| 1020 n = ptaGetCount(ptas); | |
| 1021 ptad = ptaCreate(n); | |
| 1022 for (i = 0; i < n; i++) { | |
| 1023 ptaGetIPt(ptas, i, &x, &y); | |
| 1024 pixGetPixel(pixm, x, y, &val); | |
| 1025 if (val == 1) | |
| 1026 ptaAddPt(ptad, x, y); | |
| 1027 } | |
| 1028 return ptad; | |
| 1029 } | |
| 1030 | |
| 1031 | |
| 1032 /*---------------------------------------------------------------------* | |
| 1033 * Least Squares Fit * | |
| 1034 *---------------------------------------------------------------------*/ | |
| 1035 /*! | |
| 1036 * \brief ptaGetLinearLSF() | |
| 1037 * | |
| 1038 * \param[in] pta | |
| 1039 * \param[out] pa [optional] slope a of least square fit: y = ax + b | |
| 1040 * \param[out] pb [optional] intercept b of least square fit | |
| 1041 * \param[out] pnafit [optional] numa of least square fit | |
| 1042 * \return 0 if OK, 1 on error | |
| 1043 * | |
| 1044 * <pre> | |
| 1045 * Notes: | |
| 1046 * (1) Either or both &a and &b must be input. They determine the | |
| 1047 * type of line that is fit. | |
| 1048 * (2) If both &a and &b are defined, this returns a and b that minimize: | |
| 1049 * | |
| 1050 * sum (yi - axi -b)^2 | |
| 1051 * i | |
| 1052 * | |
| 1053 * The method is simple: differentiate this expression w/rt a and b, | |
| 1054 * and solve the resulting two equations for a and b in terms of | |
| 1055 * various sums over the input data (xi, yi). | |
| 1056 * (3) We also allow two special cases, where either a = 0 or b = 0: | |
| 1057 * (a) If &a is given and &b = null, find the linear LSF that | |
| 1058 * goes through the origin (b = 0). | |
| 1059 * (b) If &b is given and &a = null, find the linear LSF with | |
| 1060 * zero slope (a = 0). | |
| 1061 * (4) If &nafit is defined, this returns an array of fitted values, | |
| 1062 * corresponding to the two implicit Numa arrays (nax and nay) in pta. | |
| 1063 * Thus, just as you can plot the data in pta as nay vs. nax, | |
| 1064 * you can plot the linear least square fit as nafit vs. nax. | |
| 1065 * Get the nax array using ptaGetArrays(pta, &nax, NULL); | |
| 1066 * </pre> | |
| 1067 */ | |
| 1068 l_ok | |
| 1069 ptaGetLinearLSF(PTA *pta, | |
| 1070 l_float32 *pa, | |
| 1071 l_float32 *pb, | |
| 1072 NUMA **pnafit) | |
| 1073 { | |
| 1074 l_int32 n, i; | |
| 1075 l_float32 a, b, factor, sx, sy, sxx, sxy, val; | |
| 1076 l_float32 *xa, *ya; | |
| 1077 | |
| 1078 if (pa) *pa = 0.0; | |
| 1079 if (pb) *pb = 0.0; | |
| 1080 if (pnafit) *pnafit = NULL; | |
| 1081 if (!pa && !pb && !pnafit) | |
| 1082 return ERROR_INT("no output requested", __func__, 1); | |
| 1083 if (!pta) | |
| 1084 return ERROR_INT("pta not defined", __func__, 1); | |
| 1085 if ((n = ptaGetCount(pta)) < 2) | |
| 1086 return ERROR_INT("less than 2 pts found", __func__, 1); | |
| 1087 | |
| 1088 xa = pta->x; /* not a copy */ | |
| 1089 ya = pta->y; /* not a copy */ | |
| 1090 sx = sy = sxx = sxy = 0.; | |
| 1091 if (pa && pb) { /* general line */ | |
| 1092 for (i = 0; i < n; i++) { | |
| 1093 sx += xa[i]; | |
| 1094 sy += ya[i]; | |
| 1095 sxx += xa[i] * xa[i]; | |
| 1096 sxy += xa[i] * ya[i]; | |
| 1097 } | |
| 1098 factor = n * sxx - sx * sx; | |
| 1099 if (factor == 0.0) | |
| 1100 return ERROR_INT("no solution found", __func__, 1); | |
| 1101 factor = 1.f / factor; | |
| 1102 | |
| 1103 a = factor * ((l_float32)n * sxy - sx * sy); | |
| 1104 b = factor * (sxx * sy - sx * sxy); | |
| 1105 } else if (pa) { /* b = 0; line through origin */ | |
| 1106 for (i = 0; i < n; i++) { | |
| 1107 sxx += xa[i] * xa[i]; | |
| 1108 sxy += xa[i] * ya[i]; | |
| 1109 } | |
| 1110 if (sxx == 0.0) | |
| 1111 return ERROR_INT("no solution found", __func__, 1); | |
| 1112 a = sxy / sxx; | |
| 1113 b = 0.0; | |
| 1114 } else { /* a = 0; horizontal line */ | |
| 1115 for (i = 0; i < n; i++) | |
| 1116 sy += ya[i]; | |
| 1117 a = 0.0; | |
| 1118 b = sy / (l_float32)n; | |
| 1119 } | |
| 1120 | |
| 1121 if (pnafit) { | |
| 1122 *pnafit = numaCreate(n); | |
| 1123 for (i = 0; i < n; i++) { | |
| 1124 val = a * xa[i] + b; | |
| 1125 numaAddNumber(*pnafit, val); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 if (pa) *pa = a; | |
| 1130 if (pb) *pb = b; | |
| 1131 return 0; | |
| 1132 } | |
| 1133 | |
| 1134 | |
| 1135 /*! | |
| 1136 * \brief ptaGetQuadraticLSF() | |
| 1137 * | |
| 1138 * \param[in] pta | |
| 1139 * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c | |
| 1140 * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c | |
| 1141 * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c | |
| 1142 * \param[out] pnafit [optional] numa of least square fit | |
| 1143 * \return 0 if OK, 1 on error | |
| 1144 * | |
| 1145 * <pre> | |
| 1146 * Notes: | |
| 1147 * (1) This does a quadratic least square fit to the set of points | |
| 1148 * in %pta. That is, it finds coefficients a, b and c that minimize: | |
| 1149 * | |
| 1150 * sum (yi - a*xi*xi -b*xi -c)^2 | |
| 1151 * i | |
| 1152 * | |
| 1153 * The method is simple: differentiate this expression w/rt | |
| 1154 * a, b and c, and solve the resulting three equations for these | |
| 1155 * coefficients in terms of various sums over the input data (xi, yi). | |
| 1156 * The three equations are in the form: | |
| 1157 * f[0][0]a + f[0][1]b + f[0][2]c = g[0] | |
| 1158 * f[1][0]a + f[1][1]b + f[1][2]c = g[1] | |
| 1159 * f[2][0]a + f[2][1]b + f[2][2]c = g[2] | |
| 1160 * (2) If &nafit is defined, this returns an array of fitted values, | |
| 1161 * corresponding to the two implicit Numa arrays (nax and nay) in pta. | |
| 1162 * Thus, just as you can plot the data in pta as nay vs. nax, | |
| 1163 * you can plot the linear least square fit as nafit vs. nax. | |
| 1164 * Get the nax array using ptaGetArrays(pta, &nax, NULL); | |
| 1165 * </pre> | |
| 1166 */ | |
| 1167 l_ok | |
| 1168 ptaGetQuadraticLSF(PTA *pta, | |
| 1169 l_float32 *pa, | |
| 1170 l_float32 *pb, | |
| 1171 l_float32 *pc, | |
| 1172 NUMA **pnafit) | |
| 1173 { | |
| 1174 l_int32 n, i, ret; | |
| 1175 l_float32 x, y, sx, sy, sx2, sx3, sx4, sxy, sx2y; | |
| 1176 l_float32 *xa, *ya; | |
| 1177 l_float32 *f[3]; | |
| 1178 l_float32 g[3]; | |
| 1179 | |
| 1180 if (pa) *pa = 0.0; | |
| 1181 if (pb) *pb = 0.0; | |
| 1182 if (pc) *pc = 0.0; | |
| 1183 if (pnafit) *pnafit = NULL; | |
| 1184 if (!pa && !pb && !pc && !pnafit) | |
| 1185 return ERROR_INT("no output requested", __func__, 1); | |
| 1186 if (!pta) | |
| 1187 return ERROR_INT("pta not defined", __func__, 1); | |
| 1188 if ((n = ptaGetCount(pta)) < 3) | |
| 1189 return ERROR_INT("less than 3 pts found", __func__, 1); | |
| 1190 | |
| 1191 xa = pta->x; /* not a copy */ | |
| 1192 ya = pta->y; /* not a copy */ | |
| 1193 sx = sy = sx2 = sx3 = sx4 = sxy = sx2y = 0.; | |
| 1194 for (i = 0; i < n; i++) { | |
| 1195 x = xa[i]; | |
| 1196 y = ya[i]; | |
| 1197 sx += x; | |
| 1198 sy += y; | |
| 1199 sx2 += x * x; | |
| 1200 sx3 += x * x * x; | |
| 1201 sx4 += x * x * x * x; | |
| 1202 sxy += x * y; | |
| 1203 sx2y += x * x * y; | |
| 1204 } | |
| 1205 | |
| 1206 for (i = 0; i < 3; i++) | |
| 1207 f[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32)); | |
| 1208 f[0][0] = sx4; | |
| 1209 f[0][1] = sx3; | |
| 1210 f[0][2] = sx2; | |
| 1211 f[1][0] = sx3; | |
| 1212 f[1][1] = sx2; | |
| 1213 f[1][2] = sx; | |
| 1214 f[2][0] = sx2; | |
| 1215 f[2][1] = sx; | |
| 1216 f[2][2] = n; | |
| 1217 g[0] = sx2y; | |
| 1218 g[1] = sxy; | |
| 1219 g[2] = sy; | |
| 1220 | |
| 1221 /* Solve for the unknowns, also putting f-inverse into f */ | |
| 1222 ret = gaussjordan(f, g, 3); | |
| 1223 for (i = 0; i < 3; i++) | |
| 1224 LEPT_FREE(f[i]); | |
| 1225 if (ret) | |
| 1226 return ERROR_INT("quadratic solution failed", __func__, 1); | |
| 1227 | |
| 1228 if (pa) *pa = g[0]; | |
| 1229 if (pb) *pb = g[1]; | |
| 1230 if (pc) *pc = g[2]; | |
| 1231 if (pnafit) { | |
| 1232 *pnafit = numaCreate(n); | |
| 1233 for (i = 0; i < n; i++) { | |
| 1234 x = xa[i]; | |
| 1235 y = g[0] * x * x + g[1] * x + g[2]; | |
| 1236 numaAddNumber(*pnafit, y); | |
| 1237 } | |
| 1238 } | |
| 1239 return 0; | |
| 1240 } | |
| 1241 | |
| 1242 | |
| 1243 /*! | |
| 1244 * \brief ptaGetCubicLSF() | |
| 1245 * | |
| 1246 * \param[in] pta | |
| 1247 * \param[out] pa [optional] coeff a of LSF: y = ax^3 + bx^2 + cx + d | |
| 1248 * \param[out] pb [optional] coeff b of LSF | |
| 1249 * \param[out] pc [optional] coeff c of LSF | |
| 1250 * \param[out] pd [optional] coeff d of LSF | |
| 1251 * \param[out] pnafit [optional] numa of least square fit | |
| 1252 * \return 0 if OK, 1 on error | |
| 1253 * | |
| 1254 * <pre> | |
| 1255 * Notes: | |
| 1256 * (1) This does a cubic least square fit to the set of points | |
| 1257 * in %pta. That is, it finds coefficients a, b, c and d | |
| 1258 * that minimize: | |
| 1259 * | |
| 1260 * sum (yi - a*xi*xi*xi -b*xi*xi -c*xi - d)^2 | |
| 1261 * i | |
| 1262 * | |
| 1263 * Differentiate this expression w/rt a, b, c and d, and solve | |
| 1264 * the resulting four equations for these coefficients in | |
| 1265 * terms of various sums over the input data (xi, yi). | |
| 1266 * The four equations are in the form: | |
| 1267 * f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] = g[0] | |
| 1268 * f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] = g[1] | |
| 1269 * f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] = g[2] | |
| 1270 * f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] = g[3] | |
| 1271 * (2) If &nafit is defined, this returns an array of fitted values, | |
| 1272 * corresponding to the two implicit Numa arrays (nax and nay) in pta. | |
| 1273 * Thus, just as you can plot the data in pta as nay vs. nax, | |
| 1274 * you can plot the linear least square fit as nafit vs. nax. | |
| 1275 * Get the nax array using ptaGetArrays(pta, &nax, NULL); | |
| 1276 * </pre> | |
| 1277 */ | |
| 1278 l_ok | |
| 1279 ptaGetCubicLSF(PTA *pta, | |
| 1280 l_float32 *pa, | |
| 1281 l_float32 *pb, | |
| 1282 l_float32 *pc, | |
| 1283 l_float32 *pd, | |
| 1284 NUMA **pnafit) | |
| 1285 { | |
| 1286 l_int32 n, i, ret; | |
| 1287 l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sxy, sx2y, sx3y; | |
| 1288 l_float32 *xa, *ya; | |
| 1289 l_float32 *f[4]; | |
| 1290 l_float32 g[4]; | |
| 1291 | |
| 1292 if (pa) *pa = 0.0; | |
| 1293 if (pb) *pb = 0.0; | |
| 1294 if (pc) *pc = 0.0; | |
| 1295 if (pd) *pd = 0.0; | |
| 1296 if (pnafit) *pnafit = NULL; | |
| 1297 if (!pa && !pb && !pc && !pd && !pnafit) | |
| 1298 return ERROR_INT("no output requested", __func__, 1); | |
| 1299 if (!pta) | |
| 1300 return ERROR_INT("pta not defined", __func__, 1); | |
| 1301 if ((n = ptaGetCount(pta)) < 4) | |
| 1302 return ERROR_INT("less than 4 pts found", __func__, 1); | |
| 1303 | |
| 1304 xa = pta->x; /* not a copy */ | |
| 1305 ya = pta->y; /* not a copy */ | |
| 1306 sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sxy = sx2y = sx3y = 0.; | |
| 1307 for (i = 0; i < n; i++) { | |
| 1308 x = xa[i]; | |
| 1309 y = ya[i]; | |
| 1310 sx += x; | |
| 1311 sy += y; | |
| 1312 sx2 += x * x; | |
| 1313 sx3 += x * x * x; | |
| 1314 sx4 += x * x * x * x; | |
| 1315 sx5 += x * x * x * x * x; | |
| 1316 sx6 += x * x * x * x * x * x; | |
| 1317 sxy += x * y; | |
| 1318 sx2y += x * x * y; | |
| 1319 sx3y += x * x * x * y; | |
| 1320 } | |
| 1321 | |
| 1322 for (i = 0; i < 4; i++) | |
| 1323 f[i] = (l_float32 *)LEPT_CALLOC(4, sizeof(l_float32)); | |
| 1324 f[0][0] = sx6; | |
| 1325 f[0][1] = sx5; | |
| 1326 f[0][2] = sx4; | |
| 1327 f[0][3] = sx3; | |
| 1328 f[1][0] = sx5; | |
| 1329 f[1][1] = sx4; | |
| 1330 f[1][2] = sx3; | |
| 1331 f[1][3] = sx2; | |
| 1332 f[2][0] = sx4; | |
| 1333 f[2][1] = sx3; | |
| 1334 f[2][2] = sx2; | |
| 1335 f[2][3] = sx; | |
| 1336 f[3][0] = sx3; | |
| 1337 f[3][1] = sx2; | |
| 1338 f[3][2] = sx; | |
| 1339 f[3][3] = n; | |
| 1340 g[0] = sx3y; | |
| 1341 g[1] = sx2y; | |
| 1342 g[2] = sxy; | |
| 1343 g[3] = sy; | |
| 1344 | |
| 1345 /* Solve for the unknowns, also putting f-inverse into f */ | |
| 1346 ret = gaussjordan(f, g, 4); | |
| 1347 for (i = 0; i < 4; i++) | |
| 1348 LEPT_FREE(f[i]); | |
| 1349 if (ret) | |
| 1350 return ERROR_INT("cubic solution failed", __func__, 1); | |
| 1351 | |
| 1352 if (pa) *pa = g[0]; | |
| 1353 if (pb) *pb = g[1]; | |
| 1354 if (pc) *pc = g[2]; | |
| 1355 if (pd) *pd = g[3]; | |
| 1356 if (pnafit) { | |
| 1357 *pnafit = numaCreate(n); | |
| 1358 for (i = 0; i < n; i++) { | |
| 1359 x = xa[i]; | |
| 1360 y = g[0] * x * x * x + g[1] * x * x + g[2] * x + g[3]; | |
| 1361 numaAddNumber(*pnafit, y); | |
| 1362 } | |
| 1363 } | |
| 1364 return 0; | |
| 1365 } | |
| 1366 | |
| 1367 | |
| 1368 /*! | |
| 1369 * \brief ptaGetQuarticLSF() | |
| 1370 * | |
| 1371 * \param[in] pta | |
| 1372 * \param[out] pa [optional] coeff a of LSF: | |
| 1373 * y = ax^4 + bx^3 + cx^2 + dx + e | |
| 1374 * \param[out] pb [optional] coeff b of LSF | |
| 1375 * \param[out] pc [optional] coeff c of LSF | |
| 1376 * \param[out] pd [optional] coeff d of LSF | |
| 1377 * \param[out] pe [optional] coeff e of LSF | |
| 1378 * \param[out] pnafit [optional] numa of least square fit | |
| 1379 * \return 0 if OK, 1 on error | |
| 1380 * | |
| 1381 * <pre> | |
| 1382 * Notes: | |
| 1383 * (1) This does a quartic least square fit to the set of points | |
| 1384 * in %pta. That is, it finds coefficients a, b, c, d and 3 | |
| 1385 * that minimize: | |
| 1386 * | |
| 1387 * sum (yi - a*xi*xi*xi*xi -b*xi*xi*xi -c*xi*xi - d*xi - e)^2 | |
| 1388 * i | |
| 1389 * | |
| 1390 * Differentiate this expression w/rt a, b, c, d and e, and solve | |
| 1391 * the resulting five equations for these coefficients in | |
| 1392 * terms of various sums over the input data (xi, yi). | |
| 1393 * The five equations are in the form: | |
| 1394 * f[0][0]a + f[0][1]b + f[0][2]c + f[0][3] + f[0][4] = g[0] | |
| 1395 * f[1][0]a + f[1][1]b + f[1][2]c + f[1][3] + f[1][4] = g[1] | |
| 1396 * f[2][0]a + f[2][1]b + f[2][2]c + f[2][3] + f[2][4] = g[2] | |
| 1397 * f[3][0]a + f[3][1]b + f[3][2]c + f[3][3] + f[3][4] = g[3] | |
| 1398 * f[4][0]a + f[4][1]b + f[4][2]c + f[4][3] + f[4][4] = g[4] | |
| 1399 * (2) If &nafit is defined, this returns an array of fitted values, | |
| 1400 * corresponding to the two implicit Numa arrays (nax and nay) in pta. | |
| 1401 * Thus, just as you can plot the data in pta as nay vs. nax, | |
| 1402 * you can plot the linear least square fit as nafit vs. nax. | |
| 1403 * Get the nax array using ptaGetArrays(pta, &nax, NULL); | |
| 1404 * </pre> | |
| 1405 */ | |
| 1406 l_ok | |
| 1407 ptaGetQuarticLSF(PTA *pta, | |
| 1408 l_float32 *pa, | |
| 1409 l_float32 *pb, | |
| 1410 l_float32 *pc, | |
| 1411 l_float32 *pd, | |
| 1412 l_float32 *pe, | |
| 1413 NUMA **pnafit) | |
| 1414 { | |
| 1415 l_int32 n, i, ret; | |
| 1416 l_float32 x, y, sx, sy, sx2, sx3, sx4, sx5, sx6, sx7, sx8; | |
| 1417 l_float32 sxy, sx2y, sx3y, sx4y; | |
| 1418 l_float32 *xa, *ya; | |
| 1419 l_float32 *f[5]; | |
| 1420 l_float32 g[5]; | |
| 1421 | |
| 1422 if (pa) *pa = 0.0; | |
| 1423 if (pb) *pb = 0.0; | |
| 1424 if (pc) *pc = 0.0; | |
| 1425 if (pd) *pd = 0.0; | |
| 1426 if (pe) *pe = 0.0; | |
| 1427 if (pnafit) *pnafit = NULL; | |
| 1428 if (!pa && !pb && !pc && !pd && !pe && !pnafit) | |
| 1429 return ERROR_INT("no output requested", __func__, 1); | |
| 1430 if (!pta) | |
| 1431 return ERROR_INT("pta not defined", __func__, 1); | |
| 1432 if ((n = ptaGetCount(pta)) < 5) | |
| 1433 return ERROR_INT("less than 5 pts found", __func__, 1); | |
| 1434 | |
| 1435 xa = pta->x; /* not a copy */ | |
| 1436 ya = pta->y; /* not a copy */ | |
| 1437 sx = sy = sx2 = sx3 = sx4 = sx5 = sx6 = sx7 = sx8 = 0; | |
| 1438 sxy = sx2y = sx3y = sx4y = 0.; | |
| 1439 for (i = 0; i < n; i++) { | |
| 1440 x = xa[i]; | |
| 1441 y = ya[i]; | |
| 1442 sx += x; | |
| 1443 sy += y; | |
| 1444 sx2 += x * x; | |
| 1445 sx3 += x * x * x; | |
| 1446 sx4 += x * x * x * x; | |
| 1447 sx5 += x * x * x * x * x; | |
| 1448 sx6 += x * x * x * x * x * x; | |
| 1449 sx7 += x * x * x * x * x * x * x; | |
| 1450 sx8 += x * x * x * x * x * x * x * x; | |
| 1451 sxy += x * y; | |
| 1452 sx2y += x * x * y; | |
| 1453 sx3y += x * x * x * y; | |
| 1454 sx4y += x * x * x * x * y; | |
| 1455 } | |
| 1456 | |
| 1457 for (i = 0; i < 5; i++) | |
| 1458 f[i] = (l_float32 *)LEPT_CALLOC(5, sizeof(l_float32)); | |
| 1459 f[0][0] = sx8; | |
| 1460 f[0][1] = sx7; | |
| 1461 f[0][2] = sx6; | |
| 1462 f[0][3] = sx5; | |
| 1463 f[0][4] = sx4; | |
| 1464 f[1][0] = sx7; | |
| 1465 f[1][1] = sx6; | |
| 1466 f[1][2] = sx5; | |
| 1467 f[1][3] = sx4; | |
| 1468 f[1][4] = sx3; | |
| 1469 f[2][0] = sx6; | |
| 1470 f[2][1] = sx5; | |
| 1471 f[2][2] = sx4; | |
| 1472 f[2][3] = sx3; | |
| 1473 f[2][4] = sx2; | |
| 1474 f[3][0] = sx5; | |
| 1475 f[3][1] = sx4; | |
| 1476 f[3][2] = sx3; | |
| 1477 f[3][3] = sx2; | |
| 1478 f[3][4] = sx; | |
| 1479 f[4][0] = sx4; | |
| 1480 f[4][1] = sx3; | |
| 1481 f[4][2] = sx2; | |
| 1482 f[4][3] = sx; | |
| 1483 f[4][4] = n; | |
| 1484 g[0] = sx4y; | |
| 1485 g[1] = sx3y; | |
| 1486 g[2] = sx2y; | |
| 1487 g[3] = sxy; | |
| 1488 g[4] = sy; | |
| 1489 | |
| 1490 /* Solve for the unknowns, also putting f-inverse into f */ | |
| 1491 ret = gaussjordan(f, g, 5); | |
| 1492 for (i = 0; i < 5; i++) | |
| 1493 LEPT_FREE(f[i]); | |
| 1494 if (ret) | |
| 1495 return ERROR_INT("quartic solution failed", __func__, 1); | |
| 1496 | |
| 1497 if (pa) *pa = g[0]; | |
| 1498 if (pb) *pb = g[1]; | |
| 1499 if (pc) *pc = g[2]; | |
| 1500 if (pd) *pd = g[3]; | |
| 1501 if (pe) *pe = g[4]; | |
| 1502 if (pnafit) { | |
| 1503 *pnafit = numaCreate(n); | |
| 1504 for (i = 0; i < n; i++) { | |
| 1505 x = xa[i]; | |
| 1506 y = g[0] * x * x * x * x + g[1] * x * x * x + g[2] * x * x | |
| 1507 + g[3] * x + g[4]; | |
| 1508 numaAddNumber(*pnafit, y); | |
| 1509 } | |
| 1510 } | |
| 1511 return 0; | |
| 1512 } | |
| 1513 | |
| 1514 | |
| 1515 /*! | |
| 1516 * \brief ptaNoisyLinearLSF() | |
| 1517 * | |
| 1518 * \param[in] pta | |
| 1519 * \param[in] factor reject outliers with error greater than this | |
| 1520 * number of medians; typically ~ 3 | |
| 1521 * \param[out] pptad [optional] with outliers removed | |
| 1522 * \param[out] pa [optional] slope a of least square fit: y = ax + b | |
| 1523 * \param[out] pb [optional] intercept b of least square fit | |
| 1524 * \param[out] pmederr [optional] median error | |
| 1525 * \param[out] pnafit [optional] numa of least square fit to ptad | |
| 1526 * \return 0 if OK, 1 on error | |
| 1527 * | |
| 1528 * <pre> | |
| 1529 * Notes: | |
| 1530 * (1) This does a linear least square fit to the set of points | |
| 1531 * in %pta. It then evaluates the errors and removes points | |
| 1532 * whose error is >= factor * median_error. It then re-runs | |
| 1533 * the linear LSF on the resulting points. | |
| 1534 * (2) Either or both &a and &b must be input. They determine the | |
| 1535 * type of line that is fit. | |
| 1536 * (3) The median error can give an indication of how good the fit | |
| 1537 * is likely to be. | |
| 1538 * </pre> | |
| 1539 */ | |
| 1540 l_ok | |
| 1541 ptaNoisyLinearLSF(PTA *pta, | |
| 1542 l_float32 factor, | |
| 1543 PTA **pptad, | |
| 1544 l_float32 *pa, | |
| 1545 l_float32 *pb, | |
| 1546 l_float32 *pmederr, | |
| 1547 NUMA **pnafit) | |
| 1548 { | |
| 1549 l_int32 n, i, ret; | |
| 1550 l_float32 x, y, yf, val, mederr; | |
| 1551 NUMA *nafit, *naerror; | |
| 1552 PTA *ptad; | |
| 1553 | |
| 1554 if (pptad) *pptad = NULL; | |
| 1555 if (pa) *pa = 0.0; | |
| 1556 if (pb) *pb = 0.0; | |
| 1557 if (pmederr) *pmederr = 0.0; | |
| 1558 if (pnafit) *pnafit = NULL; | |
| 1559 if (!pptad && !pa && !pb && !pnafit) | |
| 1560 return ERROR_INT("no output requested", __func__, 1); | |
| 1561 if (!pta) | |
| 1562 return ERROR_INT("pta not defined", __func__, 1); | |
| 1563 if (factor <= 0.0) | |
| 1564 return ERROR_INT("factor must be > 0.0", __func__, 1); | |
| 1565 if ((n = ptaGetCount(pta)) < 3) | |
| 1566 return ERROR_INT("less than 2 pts found", __func__, 1); | |
| 1567 | |
| 1568 if (ptaGetLinearLSF(pta, pa, pb, &nafit) != 0) | |
| 1569 return ERROR_INT("error in linear LSF", __func__, 1); | |
| 1570 | |
| 1571 /* Get the median error */ | |
| 1572 naerror = numaCreate(n); | |
| 1573 for (i = 0; i < n; i++) { | |
| 1574 ptaGetPt(pta, i, &x, &y); | |
| 1575 numaGetFValue(nafit, i, &yf); | |
| 1576 numaAddNumber(naerror, L_ABS(y - yf)); | |
| 1577 } | |
| 1578 numaGetMedian(naerror, &mederr); | |
| 1579 if (pmederr) *pmederr = mederr; | |
| 1580 numaDestroy(&nafit); | |
| 1581 | |
| 1582 /* Remove outliers */ | |
| 1583 ptad = ptaCreate(n); | |
| 1584 for (i = 0; i < n; i++) { | |
| 1585 ptaGetPt(pta, i, &x, &y); | |
| 1586 numaGetFValue(naerror, i, &val); | |
| 1587 if (val <= factor * mederr) /* <= in case mederr = 0 */ | |
| 1588 ptaAddPt(ptad, x, y); | |
| 1589 } | |
| 1590 numaDestroy(&naerror); | |
| 1591 | |
| 1592 /* Do LSF again */ | |
| 1593 ret = ptaGetLinearLSF(ptad, pa, pb, pnafit); | |
| 1594 if (pptad) | |
| 1595 *pptad = ptad; | |
| 1596 else | |
| 1597 ptaDestroy(&ptad); | |
| 1598 | |
| 1599 return ret; | |
| 1600 } | |
| 1601 | |
| 1602 | |
| 1603 /*! | |
| 1604 * \brief ptaNoisyQuadraticLSF() | |
| 1605 * | |
| 1606 * \param[in] pta | |
| 1607 * \param[in] factor reject outliers with error greater than this | |
| 1608 * number of medians; typically ~ 3 | |
| 1609 * \param[out] pptad [optional] with outliers removed | |
| 1610 * \param[out] pa [optional] coeff a of LSF: y = ax^2 + bx + c | |
| 1611 * \param[out] pb [optional] coeff b of LSF: y = ax^2 + bx + c | |
| 1612 * \param[out] pc [optional] coeff c of LSF: y = ax^2 + bx + c | |
| 1613 * \param[out] pmederr [optional] median error | |
| 1614 * \param[out] pnafit [optional] numa of least square fit to ptad | |
| 1615 * \return 0 if OK, 1 on error | |
| 1616 * | |
| 1617 * <pre> | |
| 1618 * Notes: | |
| 1619 * (1) This does a quadratic least square fit to the set of points | |
| 1620 * in %pta. It then evaluates the errors and removes points | |
| 1621 * whose error is >= factor * median_error. It then re-runs | |
| 1622 * a quadratic LSF on the resulting points. | |
| 1623 * </pre> | |
| 1624 */ | |
| 1625 l_ok | |
| 1626 ptaNoisyQuadraticLSF(PTA *pta, | |
| 1627 l_float32 factor, | |
| 1628 PTA **pptad, | |
| 1629 l_float32 *pa, | |
| 1630 l_float32 *pb, | |
| 1631 l_float32 *pc, | |
| 1632 l_float32 *pmederr, | |
| 1633 NUMA **pnafit) | |
| 1634 { | |
| 1635 l_int32 n, i, ret; | |
| 1636 l_float32 x, y, yf, val, mederr; | |
| 1637 NUMA *nafit, *naerror; | |
| 1638 PTA *ptad; | |
| 1639 | |
| 1640 if (pptad) *pptad = NULL; | |
| 1641 if (pa) *pa = 0.0; | |
| 1642 if (pb) *pb = 0.0; | |
| 1643 if (pc) *pc = 0.0; | |
| 1644 if (pmederr) *pmederr = 0.0; | |
| 1645 if (pnafit) *pnafit = NULL; | |
| 1646 if (!pptad && !pa && !pb && !pc && !pnafit) | |
| 1647 return ERROR_INT("no output requested", __func__, 1); | |
| 1648 if (factor <= 0.0) | |
| 1649 return ERROR_INT("factor must be > 0.0", __func__, 1); | |
| 1650 if (!pta) | |
| 1651 return ERROR_INT("pta not defined", __func__, 1); | |
| 1652 if ((n = ptaGetCount(pta)) < 3) | |
| 1653 return ERROR_INT("less than 3 pts found", __func__, 1); | |
| 1654 | |
| 1655 if (ptaGetQuadraticLSF(pta, NULL, NULL, NULL, &nafit) != 0) | |
| 1656 return ERROR_INT("error in quadratic LSF", __func__, 1); | |
| 1657 | |
| 1658 /* Get the median error */ | |
| 1659 naerror = numaCreate(n); | |
| 1660 for (i = 0; i < n; i++) { | |
| 1661 ptaGetPt(pta, i, &x, &y); | |
| 1662 numaGetFValue(nafit, i, &yf); | |
| 1663 numaAddNumber(naerror, L_ABS(y - yf)); | |
| 1664 } | |
| 1665 numaGetMedian(naerror, &mederr); | |
| 1666 if (pmederr) *pmederr = mederr; | |
| 1667 numaDestroy(&nafit); | |
| 1668 | |
| 1669 /* Remove outliers */ | |
| 1670 ptad = ptaCreate(n); | |
| 1671 for (i = 0; i < n; i++) { | |
| 1672 ptaGetPt(pta, i, &x, &y); | |
| 1673 numaGetFValue(naerror, i, &val); | |
| 1674 if (val <= factor * mederr) /* <= in case mederr = 0 */ | |
| 1675 ptaAddPt(ptad, x, y); | |
| 1676 } | |
| 1677 numaDestroy(&naerror); | |
| 1678 n = ptaGetCount(ptad); | |
| 1679 if ((n = ptaGetCount(ptad)) < 3) { | |
| 1680 ptaDestroy(&ptad); | |
| 1681 return ERROR_INT("less than 3 pts found", __func__, 1); | |
| 1682 } | |
| 1683 | |
| 1684 /* Do LSF again */ | |
| 1685 ret = ptaGetQuadraticLSF(ptad, pa, pb, pc, pnafit); | |
| 1686 if (pptad) | |
| 1687 *pptad = ptad; | |
| 1688 else | |
| 1689 ptaDestroy(&ptad); | |
| 1690 | |
| 1691 return ret; | |
| 1692 } | |
| 1693 | |
| 1694 | |
| 1695 /*! | |
| 1696 * \brief applyLinearFit() | |
| 1697 * | |
| 1698 * \param[in] a, b linear fit coefficients | |
| 1699 * \param[in] x | |
| 1700 * \param[out] py y = a * x + b | |
| 1701 * \return 0 if OK, 1 on error | |
| 1702 */ | |
| 1703 l_ok | |
| 1704 applyLinearFit(l_float32 a, | |
| 1705 l_float32 b, | |
| 1706 l_float32 x, | |
| 1707 l_float32 *py) | |
| 1708 { | |
| 1709 if (!py) | |
| 1710 return ERROR_INT("&y not defined", __func__, 1); | |
| 1711 | |
| 1712 *py = a * x + b; | |
| 1713 return 0; | |
| 1714 } | |
| 1715 | |
| 1716 | |
| 1717 /*! | |
| 1718 * \brief applyQuadraticFit() | |
| 1719 * | |
| 1720 * \param[in] a, b, c quadratic fit coefficients | |
| 1721 * \param[in] x | |
| 1722 * \param[out] py y = a * x^2 + b * x + c | |
| 1723 * \return 0 if OK, 1 on error | |
| 1724 */ | |
| 1725 l_ok | |
| 1726 applyQuadraticFit(l_float32 a, | |
| 1727 l_float32 b, | |
| 1728 l_float32 c, | |
| 1729 l_float32 x, | |
| 1730 l_float32 *py) | |
| 1731 { | |
| 1732 if (!py) | |
| 1733 return ERROR_INT("&y not defined", __func__, 1); | |
| 1734 | |
| 1735 *py = a * x * x + b * x + c; | |
| 1736 return 0; | |
| 1737 } | |
| 1738 | |
| 1739 | |
| 1740 /*! | |
| 1741 * \brief applyCubicFit() | |
| 1742 * | |
| 1743 * \param[in] a, b, c, d cubic fit coefficients | |
| 1744 * \param[in] x | |
| 1745 * \param[out] py y = a * x^3 + b * x^2 + c * x + d | |
| 1746 * \return 0 if OK, 1 on error | |
| 1747 */ | |
| 1748 l_ok | |
| 1749 applyCubicFit(l_float32 a, | |
| 1750 l_float32 b, | |
| 1751 l_float32 c, | |
| 1752 l_float32 d, | |
| 1753 l_float32 x, | |
| 1754 l_float32 *py) | |
| 1755 { | |
| 1756 if (!py) | |
| 1757 return ERROR_INT("&y not defined", __func__, 1); | |
| 1758 | |
| 1759 *py = a * x * x * x + b * x * x + c * x + d; | |
| 1760 return 0; | |
| 1761 } | |
| 1762 | |
| 1763 | |
| 1764 /*! | |
| 1765 * \brief applyQuarticFit() | |
| 1766 * | |
| 1767 * \param[in] a, b, c, d, e quartic fit coefficients | |
| 1768 * \param[in] x | |
| 1769 * \param[out] py y = a * x^4 + b * x^3 + c * x^2 + d * x + e | |
| 1770 * \return 0 if OK, 1 on error | |
| 1771 */ | |
| 1772 l_ok | |
| 1773 applyQuarticFit(l_float32 a, | |
| 1774 l_float32 b, | |
| 1775 l_float32 c, | |
| 1776 l_float32 d, | |
| 1777 l_float32 e, | |
| 1778 l_float32 x, | |
| 1779 l_float32 *py) | |
| 1780 { | |
| 1781 l_float32 x2; | |
| 1782 | |
| 1783 if (!py) | |
| 1784 return ERROR_INT("&y not defined", __func__, 1); | |
| 1785 | |
| 1786 x2 = x * x; | |
| 1787 *py = a * x2 * x2 + b * x2 * x + c * x2 + d * x + e; | |
| 1788 return 0; | |
| 1789 } | |
| 1790 | |
| 1791 | |
| 1792 /*---------------------------------------------------------------------* | |
| 1793 * Interconversions with Pix * | |
| 1794 *---------------------------------------------------------------------*/ | |
| 1795 /*! | |
| 1796 * \brief pixPlotAlongPta() | |
| 1797 * | |
| 1798 * \param[in] pixs any depth | |
| 1799 * \param[in] pta set of points on which to plot | |
| 1800 * \param[in] outformat GPLOT_PNG, GPLOT_PS, GPLOT_EPS, GPLOT_LATEX | |
| 1801 * \param[in] title [optional] for plot; can be null | |
| 1802 * \return 0 if OK, 1 on error | |
| 1803 * | |
| 1804 * <pre> | |
| 1805 * Notes: | |
| 1806 * (1) This is a debugging function. | |
| 1807 * (2) Removes existing colormaps and clips the pta to the input %pixs. | |
| 1808 * (3) If the image is RGB, three separate plots are generated. | |
| 1809 * </pre> | |
| 1810 */ | |
| 1811 l_ok | |
| 1812 pixPlotAlongPta(PIX *pixs, | |
| 1813 PTA *pta, | |
| 1814 l_int32 outformat, | |
| 1815 const char *title) | |
| 1816 { | |
| 1817 char buffer[128]; | |
| 1818 char *rtitle, *gtitle, *btitle; | |
| 1819 static l_int32 count = 0; /* require separate temp files for each call */ | |
| 1820 l_int32 i, x, y, d, w, h, npts, rval, gval, bval; | |
| 1821 l_uint32 val; | |
| 1822 NUMA *na, *nar, *nag, *nab; | |
| 1823 PIX *pixt; | |
| 1824 | |
| 1825 lept_mkdir("lept/plot"); | |
| 1826 | |
| 1827 if (!pixs) | |
| 1828 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1829 if (!pta) | |
| 1830 return ERROR_INT("pta not defined", __func__, 1); | |
| 1831 if (outformat != GPLOT_PNG && outformat != GPLOT_PS && | |
| 1832 outformat != GPLOT_EPS && outformat != GPLOT_LATEX) { | |
| 1833 L_WARNING("outformat invalid; using GPLOT_PNG\n", __func__); | |
| 1834 outformat = GPLOT_PNG; | |
| 1835 } | |
| 1836 | |
| 1837 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 1838 d = pixGetDepth(pixt); | |
| 1839 w = pixGetWidth(pixt); | |
| 1840 h = pixGetHeight(pixt); | |
| 1841 npts = ptaGetCount(pta); | |
| 1842 if (d == 32) { | |
| 1843 nar = numaCreate(npts); | |
| 1844 nag = numaCreate(npts); | |
| 1845 nab = numaCreate(npts); | |
| 1846 for (i = 0; i < npts; i++) { | |
| 1847 ptaGetIPt(pta, i, &x, &y); | |
| 1848 if (x < 0 || x >= w) | |
| 1849 continue; | |
| 1850 if (y < 0 || y >= h) | |
| 1851 continue; | |
| 1852 pixGetPixel(pixt, x, y, &val); | |
| 1853 rval = GET_DATA_BYTE(&val, COLOR_RED); | |
| 1854 gval = GET_DATA_BYTE(&val, COLOR_GREEN); | |
| 1855 bval = GET_DATA_BYTE(&val, COLOR_BLUE); | |
| 1856 numaAddNumber(nar, rval); | |
| 1857 numaAddNumber(nag, gval); | |
| 1858 numaAddNumber(nab, bval); | |
| 1859 } | |
| 1860 | |
| 1861 snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); | |
| 1862 rtitle = stringJoin("Red: ", title); | |
| 1863 gplotSimple1(nar, outformat, buffer, rtitle); | |
| 1864 snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); | |
| 1865 gtitle = stringJoin("Green: ", title); | |
| 1866 gplotSimple1(nag, outformat, buffer, gtitle); | |
| 1867 snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); | |
| 1868 btitle = stringJoin("Blue: ", title); | |
| 1869 gplotSimple1(nab, outformat, buffer, btitle); | |
| 1870 numaDestroy(&nar); | |
| 1871 numaDestroy(&nag); | |
| 1872 numaDestroy(&nab); | |
| 1873 LEPT_FREE(rtitle); | |
| 1874 LEPT_FREE(gtitle); | |
| 1875 LEPT_FREE(btitle); | |
| 1876 } else { | |
| 1877 na = numaCreate(npts); | |
| 1878 for (i = 0; i < npts; i++) { | |
| 1879 ptaGetIPt(pta, i, &x, &y); | |
| 1880 if (x < 0 || x >= w) | |
| 1881 continue; | |
| 1882 if (y < 0 || y >= h) | |
| 1883 continue; | |
| 1884 pixGetPixel(pixt, x, y, &val); | |
| 1885 numaAddNumber(na, (l_float32)val); | |
| 1886 } | |
| 1887 | |
| 1888 snprintf(buffer, sizeof(buffer), "/tmp/lept/plot/%03d", count++); | |
| 1889 gplotSimple1(na, outformat, buffer, title); | |
| 1890 numaDestroy(&na); | |
| 1891 } | |
| 1892 pixDestroy(&pixt); | |
| 1893 return 0; | |
| 1894 } | |
| 1895 | |
| 1896 | |
| 1897 /*! | |
| 1898 * \brief ptaGetPixelsFromPix() | |
| 1899 * | |
| 1900 * \param[in] pixs 1 bpp | |
| 1901 * \param[in] box [optional] can be null | |
| 1902 * \return pta, or NULL on error | |
| 1903 * | |
| 1904 * <pre> | |
| 1905 * Notes: | |
| 1906 * (1) Generates a pta of fg pixels in the pix, within the box. | |
| 1907 * If box == NULL, it uses the entire pix. | |
| 1908 * </pre> | |
| 1909 */ | |
| 1910 PTA * | |
| 1911 ptaGetPixelsFromPix(PIX *pixs, | |
| 1912 BOX *box) | |
| 1913 { | |
| 1914 l_int32 i, j, w, h, wpl, xstart, xend, ystart, yend, bw, bh; | |
| 1915 l_uint32 *data, *line; | |
| 1916 PTA *pta; | |
| 1917 | |
| 1918 if (!pixs || (pixGetDepth(pixs) != 1)) | |
| 1919 return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 1920 | |
| 1921 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1922 data = pixGetData(pixs); | |
| 1923 wpl = pixGetWpl(pixs); | |
| 1924 xstart = ystart = 0; | |
| 1925 xend = w - 1; | |
| 1926 yend = h - 1; | |
| 1927 if (box) { | |
| 1928 boxGetGeometry(box, &xstart, &ystart, &bw, &bh); | |
| 1929 xend = xstart + bw - 1; | |
| 1930 yend = ystart + bh - 1; | |
| 1931 } | |
| 1932 | |
| 1933 if ((pta = ptaCreate(0)) == NULL) | |
| 1934 return (PTA *)ERROR_PTR("pta not made", __func__, NULL); | |
| 1935 for (i = ystart; i <= yend; i++) { | |
| 1936 line = data + i * wpl; | |
| 1937 for (j = xstart; j <= xend; j++) { | |
| 1938 if (GET_DATA_BIT(line, j)) | |
| 1939 ptaAddPt(pta, j, i); | |
| 1940 } | |
| 1941 } | |
| 1942 | |
| 1943 return pta; | |
| 1944 } | |
| 1945 | |
| 1946 | |
| 1947 /*! | |
| 1948 * \brief pixGenerateFromPta() | |
| 1949 * | |
| 1950 * \param[in] pta | |
| 1951 * \param[in] w, h of pix | |
| 1952 * \return pix 1 bpp, or NULL on error | |
| 1953 * | |
| 1954 * <pre> | |
| 1955 * Notes: | |
| 1956 * (1) Points are rounded to nearest ints. | |
| 1957 * (2) Any points outside (w,h) are silently discarded. | |
| 1958 * (3) Output 1 bpp pix has values 1 for each point in the pta. | |
| 1959 * </pre> | |
| 1960 */ | |
| 1961 PIX * | |
| 1962 pixGenerateFromPta(PTA *pta, | |
| 1963 l_int32 w, | |
| 1964 l_int32 h) | |
| 1965 { | |
| 1966 l_int32 n, i, x, y; | |
| 1967 PIX *pix; | |
| 1968 | |
| 1969 if (!pta) | |
| 1970 return (PIX *)ERROR_PTR("pta not defined", __func__, NULL); | |
| 1971 | |
| 1972 if ((pix = pixCreate(w, h, 1)) == NULL) | |
| 1973 return (PIX *)ERROR_PTR("pix not made", __func__, NULL); | |
| 1974 n = ptaGetCount(pta); | |
| 1975 for (i = 0; i < n; i++) { | |
| 1976 ptaGetIPt(pta, i, &x, &y); | |
| 1977 if (x < 0 || x >= w || y < 0 || y >= h) | |
| 1978 continue; | |
| 1979 pixSetPixel(pix, x, y, 1); | |
| 1980 } | |
| 1981 | |
| 1982 return pix; | |
| 1983 } | |
| 1984 | |
| 1985 | |
| 1986 /*! | |
| 1987 * \brief ptaGetBoundaryPixels() | |
| 1988 * | |
| 1989 * \param[in] pixs 1 bpp | |
| 1990 * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG | |
| 1991 * \return pta, or NULL on error | |
| 1992 * | |
| 1993 * <pre> | |
| 1994 * Notes: | |
| 1995 * (1) This generates a pta of either fg or bg boundary pixels. | |
| 1996 * (2) See also pixGeneratePtaBoundary() for rendering of | |
| 1997 * fg boundary pixels. | |
| 1998 * </pre> | |
| 1999 */ | |
| 2000 PTA * | |
| 2001 ptaGetBoundaryPixels(PIX *pixs, | |
| 2002 l_int32 type) | |
| 2003 { | |
| 2004 PIX *pixt; | |
| 2005 PTA *pta; | |
| 2006 | |
| 2007 if (!pixs || (pixGetDepth(pixs) != 1)) | |
| 2008 return (PTA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 2009 if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) | |
| 2010 return (PTA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 2011 | |
| 2012 if (type == L_BOUNDARY_FG) | |
| 2013 pixt = pixMorphSequence(pixs, "e3.3", 0); | |
| 2014 else | |
| 2015 pixt = pixMorphSequence(pixs, "d3.3", 0); | |
| 2016 pixXor(pixt, pixt, pixs); | |
| 2017 pta = ptaGetPixelsFromPix(pixt, NULL); | |
| 2018 | |
| 2019 pixDestroy(&pixt); | |
| 2020 return pta; | |
| 2021 } | |
| 2022 | |
| 2023 | |
| 2024 /*! | |
| 2025 * \brief ptaaGetBoundaryPixels() | |
| 2026 * | |
| 2027 * \param[in] pixs 1 bpp | |
| 2028 * \param[in] type L_BOUNDARY_FG, L_BOUNDARY_BG | |
| 2029 * \param[in] connectivity 4 or 8 | |
| 2030 * \param[out] pboxa [optional] bounding boxes of the c.c. | |
| 2031 * \param[out] ppixa [optional] pixa of the c.c. | |
| 2032 * \return ptaa, or NULL on error | |
| 2033 * | |
| 2034 * <pre> | |
| 2035 * Notes: | |
| 2036 * (1) This generates a ptaa of either fg or bg boundary pixels, | |
| 2037 * where each pta has the boundary pixels for a connected | |
| 2038 * component. | |
| 2039 * (2) We can't simply find all the boundary pixels and then select | |
| 2040 * those within the bounding box of each component, because | |
| 2041 * bounding boxes can overlap. It is necessary to extract and | |
| 2042 * dilate or erode each component separately. Note also that | |
| 2043 * special handling is required for bg pixels when the | |
| 2044 * component touches the pix boundary. | |
| 2045 * </pre> | |
| 2046 */ | |
| 2047 PTAA * | |
| 2048 ptaaGetBoundaryPixels(PIX *pixs, | |
| 2049 l_int32 type, | |
| 2050 l_int32 connectivity, | |
| 2051 BOXA **pboxa, | |
| 2052 PIXA **ppixa) | |
| 2053 { | |
| 2054 l_int32 i, n, w, h, x, y, bw, bh, left, right, top, bot; | |
| 2055 BOXA *boxa; | |
| 2056 PIX *pixt1, *pixt2; | |
| 2057 PIXA *pixa; | |
| 2058 PTA *pta1, *pta2; | |
| 2059 PTAA *ptaa; | |
| 2060 | |
| 2061 if (pboxa) *pboxa = NULL; | |
| 2062 if (ppixa) *ppixa = NULL; | |
| 2063 if (!pixs || (pixGetDepth(pixs) != 1)) | |
| 2064 return (PTAA *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 2065 if (type != L_BOUNDARY_FG && type != L_BOUNDARY_BG) | |
| 2066 return (PTAA *)ERROR_PTR("invalid type", __func__, NULL); | |
| 2067 if (connectivity != 4 && connectivity != 8) | |
| 2068 return (PTAA *)ERROR_PTR("connectivity not 4 or 8", __func__, NULL); | |
| 2069 | |
| 2070 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2071 boxa = pixConnComp(pixs, &pixa, connectivity); | |
| 2072 n = boxaGetCount(boxa); | |
| 2073 ptaa = ptaaCreate(0); | |
| 2074 for (i = 0; i < n; i++) { | |
| 2075 pixt1 = pixaGetPix(pixa, i, L_CLONE); | |
| 2076 boxaGetBoxGeometry(boxa, i, &x, &y, &bw, &bh); | |
| 2077 left = right = top = bot = 0; | |
| 2078 if (type == L_BOUNDARY_BG) { | |
| 2079 if (x > 0) left = 1; | |
| 2080 if (y > 0) top = 1; | |
| 2081 if (x + bw < w) right = 1; | |
| 2082 if (y + bh < h) bot = 1; | |
| 2083 pixt2 = pixAddBorderGeneral(pixt1, left, right, top, bot, 0); | |
| 2084 } else { | |
| 2085 pixt2 = pixClone(pixt1); | |
| 2086 } | |
| 2087 pta1 = ptaGetBoundaryPixels(pixt2, type); | |
| 2088 pta2 = ptaTransform(pta1, x - left, y - top, 1.0, 1.0); | |
| 2089 ptaaAddPta(ptaa, pta2, L_INSERT); | |
| 2090 ptaDestroy(&pta1); | |
| 2091 pixDestroy(&pixt1); | |
| 2092 pixDestroy(&pixt2); | |
| 2093 } | |
| 2094 | |
| 2095 if (pboxa) | |
| 2096 *pboxa = boxa; | |
| 2097 else | |
| 2098 boxaDestroy(&boxa); | |
| 2099 if (ppixa) | |
| 2100 *ppixa = pixa; | |
| 2101 else | |
| 2102 pixaDestroy(&pixa); | |
| 2103 return ptaa; | |
| 2104 } | |
| 2105 | |
| 2106 | |
| 2107 /*! | |
| 2108 * \brief ptaaIndexLabeledPixels() | |
| 2109 * | |
| 2110 * \param[in] pixs 32 bpp, of indices of c.c. | |
| 2111 * \param[out] pncc [optional] number of connected components | |
| 2112 * \return ptaa, or NULL on error | |
| 2113 * | |
| 2114 * <pre> | |
| 2115 * Notes: | |
| 2116 * (1) The pixel values in %pixs are the index of the connected component | |
| 2117 * to which the pixel belongs; %pixs is typically generated from | |
| 2118 * a 1 bpp pix by pixConnCompTransform(). Background pixels in | |
| 2119 * the generating 1 bpp pix are represented in %pixs by 0. | |
| 2120 * We do not check that the pixel values are correctly labelled. | |
| 2121 * (2) Each pta in the returned ptaa gives the pixel locations | |
| 2122 * corresponding to a connected component, with the label of each | |
| 2123 * given by the index of the pta into the ptaa. | |
| 2124 * (3) Initialize with the first pta in ptaa being empty and | |
| 2125 * representing the background value (index 0) in the pix. | |
| 2126 * </pre> | |
| 2127 */ | |
| 2128 PTAA * | |
| 2129 ptaaIndexLabeledPixels(PIX *pixs, | |
| 2130 l_int32 *pncc) | |
| 2131 { | |
| 2132 l_int32 wpl, index, i, j, w, h; | |
| 2133 l_uint32 maxval; | |
| 2134 l_uint32 *data, *line; | |
| 2135 PTA *pta; | |
| 2136 PTAA *ptaa; | |
| 2137 | |
| 2138 if (pncc) *pncc = 0; | |
| 2139 if (!pixs || (pixGetDepth(pixs) != 32)) | |
| 2140 return (PTAA *)ERROR_PTR("pixs undef or not 32 bpp", __func__, NULL); | |
| 2141 | |
| 2142 /* The number of c.c. is the maximum pixel value. Use this to | |
| 2143 * initialize ptaa with sufficient pta arrays */ | |
| 2144 pixGetMaxValueInRect(pixs, NULL, &maxval, NULL, NULL); | |
| 2145 if (pncc) *pncc = maxval; | |
| 2146 pta = ptaCreate(1); | |
| 2147 ptaa = ptaaCreate(maxval + 1); | |
| 2148 ptaaInitFull(ptaa, pta); | |
| 2149 ptaDestroy(&pta); | |
| 2150 | |
| 2151 /* Sweep over %pixs, saving the pixel coordinates of each pixel | |
| 2152 * with nonzero value in the appropriate pta, indexed by that value. */ | |
| 2153 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2154 data = pixGetData(pixs); | |
| 2155 wpl = pixGetWpl(pixs); | |
| 2156 for (i = 0; i < h; i++) { | |
| 2157 line = data + wpl * i; | |
| 2158 for (j = 0; j < w; j++) { | |
| 2159 index = line[j]; | |
| 2160 if (index > 0) | |
| 2161 ptaaAddPt(ptaa, index, j, i); | |
| 2162 } | |
| 2163 } | |
| 2164 | |
| 2165 return ptaa; | |
| 2166 } | |
| 2167 | |
| 2168 | |
| 2169 /*! | |
| 2170 * \brief ptaGetNeighborPixLocs() | |
| 2171 * | |
| 2172 * \param[in] pixs any depth | |
| 2173 * \param[in] x, y pixel from which we search for nearest neighbors | |
| 2174 * \param[in] conn 4 or 8 connectivity | |
| 2175 * \return pta, or NULL on error | |
| 2176 * | |
| 2177 * <pre> | |
| 2178 * Notes: | |
| 2179 * (1) Generates a pta of all valid neighbor pixel locations, | |
| 2180 * or NULL on error. | |
| 2181 * </pre> | |
| 2182 */ | |
| 2183 PTA * | |
| 2184 ptaGetNeighborPixLocs(PIX *pixs, | |
| 2185 l_int32 x, | |
| 2186 l_int32 y, | |
| 2187 l_int32 conn) | |
| 2188 { | |
| 2189 l_int32 w, h; | |
| 2190 PTA *pta; | |
| 2191 | |
| 2192 if (!pixs) | |
| 2193 return (PTA *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2194 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2195 if (x < 0 || x >= w || y < 0 || y >= h) | |
| 2196 return (PTA *)ERROR_PTR("(x,y) not in pixs", __func__, NULL); | |
| 2197 if (conn != 4 && conn != 8) | |
| 2198 return (PTA *)ERROR_PTR("conn not 4 or 8", __func__, NULL); | |
| 2199 | |
| 2200 pta = ptaCreate(conn); | |
| 2201 if (x > 0) | |
| 2202 ptaAddPt(pta, x - 1, y); | |
| 2203 if (x < w - 1) | |
| 2204 ptaAddPt(pta, x + 1, y); | |
| 2205 if (y > 0) | |
| 2206 ptaAddPt(pta, x, y - 1); | |
| 2207 if (y < h - 1) | |
| 2208 ptaAddPt(pta, x, y + 1); | |
| 2209 if (conn == 8) { | |
| 2210 if (x > 0) { | |
| 2211 if (y > 0) | |
| 2212 ptaAddPt(pta, x - 1, y - 1); | |
| 2213 if (y < h - 1) | |
| 2214 ptaAddPt(pta, x - 1, y + 1); | |
| 2215 } | |
| 2216 if (x < w - 1) { | |
| 2217 if (y > 0) | |
| 2218 ptaAddPt(pta, x + 1, y - 1); | |
| 2219 if (y < h - 1) | |
| 2220 ptaAddPt(pta, x + 1, y + 1); | |
| 2221 } | |
| 2222 } | |
| 2223 | |
| 2224 return pta; | |
| 2225 } | |
| 2226 | |
| 2227 | |
| 2228 /*---------------------------------------------------------------------* | |
| 2229 * Interconversion with Numa * | |
| 2230 *---------------------------------------------------------------------*/ | |
| 2231 /*! | |
| 2232 * \brief numaConvertToPta1() | |
| 2233 * | |
| 2234 * \param[in] na numa with implicit y(x) | |
| 2235 * \return pta if OK; null on error | |
| 2236 */ | |
| 2237 PTA * | |
| 2238 numaConvertToPta1(NUMA *na) | |
| 2239 { | |
| 2240 l_int32 i, n; | |
| 2241 l_float32 startx, delx, val; | |
| 2242 PTA *pta; | |
| 2243 | |
| 2244 if (!na) | |
| 2245 return (PTA *)ERROR_PTR("na not defined", __func__, NULL); | |
| 2246 | |
| 2247 n = numaGetCount(na); | |
| 2248 pta = ptaCreate(n); | |
| 2249 numaGetParameters(na, &startx, &delx); | |
| 2250 for (i = 0; i < n; i++) { | |
| 2251 numaGetFValue(na, i, &val); | |
| 2252 ptaAddPt(pta, startx + i * delx, val); | |
| 2253 } | |
| 2254 return pta; | |
| 2255 } | |
| 2256 | |
| 2257 | |
| 2258 /*! | |
| 2259 * \brief numaConvertToPta2() | |
| 2260 * | |
| 2261 * \param[in] nax | |
| 2262 * \param[in] nay | |
| 2263 * \return pta if OK; null on error | |
| 2264 */ | |
| 2265 PTA * | |
| 2266 numaConvertToPta2(NUMA *nax, | |
| 2267 NUMA *nay) | |
| 2268 { | |
| 2269 l_int32 i, n, nx, ny; | |
| 2270 l_float32 valx, valy; | |
| 2271 PTA *pta; | |
| 2272 | |
| 2273 if (!nax || !nay) | |
| 2274 return (PTA *)ERROR_PTR("nax and nay not both defined", __func__, NULL); | |
| 2275 | |
| 2276 nx = numaGetCount(nax); | |
| 2277 ny = numaGetCount(nay); | |
| 2278 n = L_MIN(nx, ny); | |
| 2279 if (nx != ny) | |
| 2280 L_WARNING("nx = %d does not equal ny = %d\n", __func__, nx, ny); | |
| 2281 pta = ptaCreate(n); | |
| 2282 for (i = 0; i < n; i++) { | |
| 2283 numaGetFValue(nax, i, &valx); | |
| 2284 numaGetFValue(nay, i, &valy); | |
| 2285 ptaAddPt(pta, valx, valy); | |
| 2286 } | |
| 2287 return pta; | |
| 2288 } | |
| 2289 | |
| 2290 | |
| 2291 /*! | |
| 2292 * \brief ptaConvertToNuma() | |
| 2293 * | |
| 2294 * \param[in] pta | |
| 2295 * \param[out] pnax addr of nax | |
| 2296 * \param[out] pnay addr of nay | |
| 2297 * \return 0 if OK, 1 on error | |
| 2298 */ | |
| 2299 l_ok | |
| 2300 ptaConvertToNuma(PTA *pta, | |
| 2301 NUMA **pnax, | |
| 2302 NUMA **pnay) | |
| 2303 { | |
| 2304 l_int32 i, n; | |
| 2305 l_float32 valx, valy; | |
| 2306 | |
| 2307 if (pnax) *pnax = NULL; | |
| 2308 if (pnay) *pnay = NULL; | |
| 2309 if (!pnax || !pnay) | |
| 2310 return ERROR_INT("&nax and &nay not both defined", __func__, 1); | |
| 2311 if (!pta) | |
| 2312 return ERROR_INT("pta not defined", __func__, 1); | |
| 2313 | |
| 2314 n = ptaGetCount(pta); | |
| 2315 *pnax = numaCreate(n); | |
| 2316 *pnay = numaCreate(n); | |
| 2317 for (i = 0; i < n; i++) { | |
| 2318 ptaGetPt(pta, i, &valx, &valy); | |
| 2319 numaAddNumber(*pnax, valx); | |
| 2320 numaAddNumber(*pnay, valy); | |
| 2321 } | |
| 2322 return 0; | |
| 2323 } | |
| 2324 | |
| 2325 | |
| 2326 /*---------------------------------------------------------------------* | |
| 2327 * Display Pta and Ptaa * | |
| 2328 *---------------------------------------------------------------------*/ | |
| 2329 /*! | |
| 2330 * \brief pixDisplayPta() | |
| 2331 * | |
| 2332 * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place | |
| 2333 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp | |
| 2334 * \param[in] pta of path to be plotted | |
| 2335 * \return pixd 32 bpp RGB version of pixs, with path in green. | |
| 2336 * | |
| 2337 * <pre> | |
| 2338 * Notes: | |
| 2339 * (1) To write on an existing pixs, pixs must be 32 bpp and | |
| 2340 * call with pixd == pixs: | |
| 2341 * pixDisplayPta(pixs, pixs, pta); | |
| 2342 * To write to a new pix, use pixd == NULL and call: | |
| 2343 * pixd = pixDisplayPta(NULL, pixs, pta); | |
| 2344 * (2) On error, returns pixd to avoid losing pixs if called as | |
| 2345 * pixs = pixDisplayPta(pixs, pixs, pta); | |
| 2346 * </pre> | |
| 2347 */ | |
| 2348 PIX * | |
| 2349 pixDisplayPta(PIX *pixd, | |
| 2350 PIX *pixs, | |
| 2351 PTA *pta) | |
| 2352 { | |
| 2353 l_int32 i, n, w, h, x, y; | |
| 2354 l_uint32 rpixel, gpixel, bpixel; | |
| 2355 | |
| 2356 if (!pixs) | |
| 2357 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2358 if (!pta) | |
| 2359 return (PIX *)ERROR_PTR("pta not defined", __func__, pixd); | |
| 2360 if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) | |
| 2361 return (PIX *)ERROR_PTR("invalid pixd", __func__, pixd); | |
| 2362 | |
| 2363 if (!pixd) | |
| 2364 pixd = pixConvertTo32(pixs); | |
| 2365 pixGetDimensions(pixd, &w, &h, NULL); | |
| 2366 composeRGBPixel(255, 0, 0, &rpixel); /* start point */ | |
| 2367 composeRGBPixel(0, 255, 0, &gpixel); | |
| 2368 composeRGBPixel(0, 0, 255, &bpixel); /* end point */ | |
| 2369 | |
| 2370 n = ptaGetCount(pta); | |
| 2371 for (i = 0; i < n; i++) { | |
| 2372 ptaGetIPt(pta, i, &x, &y); | |
| 2373 if (x < 0 || x >= w || y < 0 || y >= h) | |
| 2374 continue; | |
| 2375 if (i == 0) | |
| 2376 pixSetPixel(pixd, x, y, rpixel); | |
| 2377 else if (i < n - 1) | |
| 2378 pixSetPixel(pixd, x, y, gpixel); | |
| 2379 else | |
| 2380 pixSetPixel(pixd, x, y, bpixel); | |
| 2381 } | |
| 2382 | |
| 2383 return pixd; | |
| 2384 } | |
| 2385 | |
| 2386 | |
| 2387 /*! | |
| 2388 * \brief pixDisplayPtaaPattern() | |
| 2389 * | |
| 2390 * \param[in] pixd 32 bpp | |
| 2391 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp; 32 bpp if in place | |
| 2392 * \param[in] ptaa giving locations at which the pattern is displayed | |
| 2393 * \param[in] pixp 1 bpp pattern to be placed such that its reference | |
| 2394 * point co-locates with each point in pta | |
| 2395 * \param[in] cx, cy reference point in pattern | |
| 2396 * \return pixd 32 bpp RGB version of pixs. | |
| 2397 * | |
| 2398 * <pre> | |
| 2399 * Notes: | |
| 2400 * (1) To write on an existing pixs, pixs must be 32 bpp and | |
| 2401 * call with pixd == pixs: | |
| 2402 * pixDisplayPtaPattern(pixs, pixs, pta, ...); | |
| 2403 * To write to a new pix, use pixd == NULL and call: | |
| 2404 * pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...); | |
| 2405 * (2) Puts a random color on each pattern associated with a pta. | |
| 2406 * (3) On error, returns pixd to avoid losing pixs if called as | |
| 2407 * pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...); | |
| 2408 * (4) A typical pattern to be used is a circle, generated with | |
| 2409 * generatePtaFilledCircle() | |
| 2410 * </pre> | |
| 2411 */ | |
| 2412 PIX * | |
| 2413 pixDisplayPtaaPattern(PIX *pixd, | |
| 2414 PIX *pixs, | |
| 2415 PTAA *ptaa, | |
| 2416 PIX *pixp, | |
| 2417 l_int32 cx, | |
| 2418 l_int32 cy) | |
| 2419 { | |
| 2420 l_int32 i, n; | |
| 2421 l_uint32 color; | |
| 2422 PIXCMAP *cmap; | |
| 2423 PTA *pta; | |
| 2424 | |
| 2425 if (!pixs) | |
| 2426 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2427 if (!ptaa) | |
| 2428 return (PIX *)ERROR_PTR("ptaa not defined", __func__, pixd); | |
| 2429 if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) | |
| 2430 return (PIX *)ERROR_PTR("invalid pixd", __func__, pixd); | |
| 2431 if (!pixp) | |
| 2432 return (PIX *)ERROR_PTR("pixp not defined", __func__, pixd); | |
| 2433 | |
| 2434 if (!pixd) | |
| 2435 pixd = pixConvertTo32(pixs); | |
| 2436 | |
| 2437 /* Use 256 random colors */ | |
| 2438 cmap = pixcmapCreateRandom(8, 0, 0); | |
| 2439 n = ptaaGetCount(ptaa); | |
| 2440 for (i = 0; i < n; i++) { | |
| 2441 pixcmapGetColor32(cmap, i % 256, &color); | |
| 2442 pta = ptaaGetPta(ptaa, i, L_CLONE); | |
| 2443 pixDisplayPtaPattern(pixd, pixd, pta, pixp, cx, cy, color); | |
| 2444 ptaDestroy(&pta); | |
| 2445 } | |
| 2446 | |
| 2447 pixcmapDestroy(&cmap); | |
| 2448 return pixd; | |
| 2449 } | |
| 2450 | |
| 2451 | |
| 2452 /*! | |
| 2453 * \brief pixDisplayPtaPattern() | |
| 2454 * | |
| 2455 * \param[in] pixd can be same as pixs or NULL; 32 bpp if in-place | |
| 2456 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp | |
| 2457 * \param[in] pta giving locations at which the pattern is displayed | |
| 2458 * \param[in] pixp 1 bpp pattern to be placed such that its reference | |
| 2459 * point co-locates with each point in pta | |
| 2460 * \param[in] cx, cy reference point in pattern | |
| 2461 * \param[in] color in 0xrrggbb00 format | |
| 2462 * \return pixd 32 bpp RGB version of pixs. | |
| 2463 * | |
| 2464 * <pre> | |
| 2465 * Notes: | |
| 2466 * (1) To write on an existing pixs, pixs must be 32 bpp and | |
| 2467 * call with pixd == pixs: | |
| 2468 * pixDisplayPtaPattern(pixs, pixs, pta, ...); | |
| 2469 * To write to a new pix, use pixd == NULL and call: | |
| 2470 * pixd = pixDisplayPtaPattern(NULL, pixs, pta, ...); | |
| 2471 * (2) On error, returns pixd to avoid losing pixs if called as | |
| 2472 * pixs = pixDisplayPtaPattern(pixs, pixs, pta, ...); | |
| 2473 * (3) A typical pattern to be used is a circle, generated with | |
| 2474 * generatePtaFilledCircle() | |
| 2475 * </pre> | |
| 2476 */ | |
| 2477 PIX * | |
| 2478 pixDisplayPtaPattern(PIX *pixd, | |
| 2479 PIX *pixs, | |
| 2480 PTA *pta, | |
| 2481 PIX *pixp, | |
| 2482 l_int32 cx, | |
| 2483 l_int32 cy, | |
| 2484 l_uint32 color) | |
| 2485 { | |
| 2486 l_int32 i, n, w, h, x, y; | |
| 2487 PTA *ptat; | |
| 2488 | |
| 2489 if (!pixs) | |
| 2490 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2491 if (!pta) | |
| 2492 return (PIX *)ERROR_PTR("pta not defined", __func__, pixd); | |
| 2493 if (pixd && (pixd != pixs || pixGetDepth(pixd) != 32)) | |
| 2494 return (PIX *)ERROR_PTR("invalid pixd", __func__, pixd); | |
| 2495 if (!pixp) | |
| 2496 return (PIX *)ERROR_PTR("pixp not defined", __func__, pixd); | |
| 2497 | |
| 2498 if (!pixd) | |
| 2499 pixd = pixConvertTo32(pixs); | |
| 2500 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2501 ptat = ptaReplicatePattern(pta, pixp, NULL, cx, cy, w, h); | |
| 2502 | |
| 2503 n = ptaGetCount(ptat); | |
| 2504 for (i = 0; i < n; i++) { | |
| 2505 ptaGetIPt(ptat, i, &x, &y); | |
| 2506 if (x < 0 || x >= w || y < 0 || y >= h) | |
| 2507 continue; | |
| 2508 pixSetPixel(pixd, x, y, color); | |
| 2509 } | |
| 2510 | |
| 2511 ptaDestroy(&ptat); | |
| 2512 return pixd; | |
| 2513 } | |
| 2514 | |
| 2515 | |
| 2516 /*! | |
| 2517 * \brief ptaReplicatePattern() | |
| 2518 * | |
| 2519 * \param[in] ptas "sparse" input pta | |
| 2520 * \param[in] pixp [optional] 1 bpp pattern, to be replicated | |
| 2521 * in output pta | |
| 2522 * \param[in] ptap [optional] set of pts, to be replicated in output pta | |
| 2523 * \param[in] cx, cy reference point in pattern | |
| 2524 * \param[in] w, h clipping sizes for output pta | |
| 2525 * \return ptad with all points of replicated pattern, or NULL on error | |
| 2526 * | |
| 2527 * <pre> | |
| 2528 * Notes: | |
| 2529 * (1) You can use either the image %pixp or the set of pts %ptap. | |
| 2530 * (2) The pattern is placed with its reference point at each point | |
| 2531 * in ptas, and all the fg pixels are colleced into ptad. | |
| 2532 * For %pixp, this is equivalent to blitting pixp at each point | |
| 2533 * in ptas, and then converting the resulting pix to a pta. | |
| 2534 * </pre> | |
| 2535 */ | |
| 2536 PTA * | |
| 2537 ptaReplicatePattern(PTA *ptas, | |
| 2538 PIX *pixp, | |
| 2539 PTA *ptap, | |
| 2540 l_int32 cx, | |
| 2541 l_int32 cy, | |
| 2542 l_int32 w, | |
| 2543 l_int32 h) | |
| 2544 { | |
| 2545 l_int32 i, j, n, np, x, y, xp, yp, xf, yf; | |
| 2546 PTA *ptat, *ptad; | |
| 2547 | |
| 2548 if (!ptas) | |
| 2549 return (PTA *)ERROR_PTR("ptas not defined", __func__, NULL); | |
| 2550 if (!pixp && !ptap) | |
| 2551 return (PTA *)ERROR_PTR("no pattern is defined", __func__, NULL); | |
| 2552 if (pixp && ptap) | |
| 2553 L_WARNING("pixp and ptap defined; using ptap\n", __func__); | |
| 2554 | |
| 2555 n = ptaGetCount(ptas); | |
| 2556 ptad = ptaCreate(n); | |
| 2557 if (ptap) | |
| 2558 ptat = ptaClone(ptap); | |
| 2559 else | |
| 2560 ptat = ptaGetPixelsFromPix(pixp, NULL); | |
| 2561 np = ptaGetCount(ptat); | |
| 2562 for (i = 0; i < n; i++) { | |
| 2563 ptaGetIPt(ptas, i, &x, &y); | |
| 2564 for (j = 0; j < np; j++) { | |
| 2565 ptaGetIPt(ptat, j, &xp, &yp); | |
| 2566 xf = x - cx + xp; | |
| 2567 yf = y - cy + yp; | |
| 2568 if (xf >= 0 && xf < w && yf >= 0 && yf < h) | |
| 2569 ptaAddPt(ptad, xf, yf); | |
| 2570 } | |
| 2571 } | |
| 2572 | |
| 2573 ptaDestroy(&ptat); | |
| 2574 return ptad; | |
| 2575 } | |
| 2576 | |
| 2577 | |
| 2578 /*! | |
| 2579 * \brief pixDisplayPtaa() | |
| 2580 * | |
| 2581 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp | |
| 2582 * \param[in] ptaa array of paths to be plotted | |
| 2583 * \return pixd 32 bpp RGB version of pixs, with paths plotted | |
| 2584 * in different colors, or NULL on error | |
| 2585 */ | |
| 2586 PIX * | |
| 2587 pixDisplayPtaa(PIX *pixs, | |
| 2588 PTAA *ptaa) | |
| 2589 { | |
| 2590 l_int32 i, j, w, h, npta, npt, x, y, rv, gv, bv; | |
| 2591 l_uint32 *pixela; | |
| 2592 NUMA *na1, *na2, *na3; | |
| 2593 PIX *pixd; | |
| 2594 PTA *pta; | |
| 2595 | |
| 2596 if (!pixs) | |
| 2597 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2598 if (!ptaa) | |
| 2599 return (PIX *)ERROR_PTR("ptaa not defined", __func__, NULL); | |
| 2600 npta = ptaaGetCount(ptaa); | |
| 2601 if (npta == 0) | |
| 2602 return (PIX *)ERROR_PTR("no pta", __func__, NULL); | |
| 2603 | |
| 2604 if ((pixd = pixConvertTo32(pixs)) == NULL) | |
| 2605 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2606 pixGetDimensions(pixd, &w, &h, NULL); | |
| 2607 | |
| 2608 /* Make a colormap for the paths */ | |
| 2609 if ((pixela = (l_uint32 *)LEPT_CALLOC(npta, sizeof(l_uint32))) == NULL) { | |
| 2610 pixDestroy(&pixd); | |
| 2611 return (PIX *)ERROR_PTR("calloc fail for pixela", __func__, NULL); | |
| 2612 } | |
| 2613 na1 = numaPseudorandomSequence(256, 14657); | |
| 2614 na2 = numaPseudorandomSequence(256, 34631); | |
| 2615 na3 = numaPseudorandomSequence(256, 54617); | |
| 2616 for (i = 0; i < npta; i++) { | |
| 2617 numaGetIValue(na1, i % 256, &rv); | |
| 2618 numaGetIValue(na2, i % 256, &gv); | |
| 2619 numaGetIValue(na3, i % 256, &bv); | |
| 2620 composeRGBPixel(rv, gv, bv, &pixela[i]); | |
| 2621 } | |
| 2622 numaDestroy(&na1); | |
| 2623 numaDestroy(&na2); | |
| 2624 numaDestroy(&na3); | |
| 2625 | |
| 2626 for (i = 0; i < npta; i++) { | |
| 2627 pta = ptaaGetPta(ptaa, i, L_CLONE); | |
| 2628 npt = ptaGetCount(pta); | |
| 2629 for (j = 0; j < npt; j++) { | |
| 2630 ptaGetIPt(pta, j, &x, &y); | |
| 2631 if (x < 0 || x >= w || y < 0 || y >= h) | |
| 2632 continue; | |
| 2633 pixSetPixel(pixd, x, y, pixela[i]); | |
| 2634 } | |
| 2635 ptaDestroy(&pta); | |
| 2636 } | |
| 2637 | |
| 2638 LEPT_FREE(pixela); | |
| 2639 return pixd; | |
| 2640 } |
