Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/kernel.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 kernel.c | |
| 30 * <pre> | |
| 31 * | |
| 32 * Basic operations on kernels for image convolution | |
| 33 * | |
| 34 * Create/destroy/copy | |
| 35 * L_KERNEL *kernelCreate() | |
| 36 * void kernelDestroy() | |
| 37 * L_KERNEL *kernelCopy() | |
| 38 * | |
| 39 * Accessors: | |
| 40 * l_int32 kernelGetElement() | |
| 41 * l_int32 kernelSetElement() | |
| 42 * l_int32 kernelGetParameters() | |
| 43 * l_int32 kernelSetOrigin() | |
| 44 * l_int32 kernelGetSum() | |
| 45 * l_int32 kernelGetMinMax() | |
| 46 * | |
| 47 * Normalize/invert | |
| 48 * L_KERNEL *kernelNormalize() | |
| 49 * L_KERNEL *kernelInvert() | |
| 50 * | |
| 51 * Helper function | |
| 52 * l_float32 **create2dFloatArray() | |
| 53 * | |
| 54 * Serialized I/O | |
| 55 * L_KERNEL *kernelRead() | |
| 56 * L_KERNEL *kernelReadStream() | |
| 57 * l_int32 kernelWrite() | |
| 58 * l_int32 kernelWriteStream() | |
| 59 * | |
| 60 * Making a kernel from a compiled string | |
| 61 * L_KERNEL *kernelCreateFromString() | |
| 62 * | |
| 63 * Making a kernel from a simple file format | |
| 64 * L_KERNEL *kernelCreateFromFile() | |
| 65 * | |
| 66 * Making a kernel from a Pix | |
| 67 * L_KERNEL *kernelCreateFromPix() | |
| 68 * | |
| 69 * Display a kernel in a pix | |
| 70 * PIX *kernelDisplayInPix() | |
| 71 * | |
| 72 * Parse string to extract numbers | |
| 73 * NUMA *parseStringForNumbers() | |
| 74 * | |
| 75 * Simple parametric kernels | |
| 76 * L_KERNEL *makeFlatKernel() | |
| 77 * L_KERNEL *makeGaussianKernel() | |
| 78 * L_KERNEL *makeGaussianKernelSep() | |
| 79 * L_KERNEL *makeDoGKernel() | |
| 80 * </pre> | |
| 81 */ | |
| 82 | |
| 83 #ifdef HAVE_CONFIG_H | |
| 84 #include <config_auto.h> | |
| 85 #endif /* HAVE_CONFIG_H */ | |
| 86 | |
| 87 #include <string.h> | |
| 88 #include <math.h> | |
| 89 #include "allheaders.h" | |
| 90 | |
| 91 /* Array size must be > 0 and not larger than this */ | |
| 92 static const l_uint32 MaxArraySize = 100000; | |
| 93 | |
| 94 /*------------------------------------------------------------------------* | |
| 95 * Create / Destroy * | |
| 96 *------------------------------------------------------------------------*/ | |
| 97 /*! | |
| 98 * \brief kernelCreate() | |
| 99 * | |
| 100 * \param[in] height, width | |
| 101 * \return kernel, or NULL on error | |
| 102 * | |
| 103 * <pre> | |
| 104 * Notes: | |
| 105 * (1) kernelCreate() initializes all values to 0. | |
| 106 * (2) After this call, (cy,cx) and nonzero data values must be | |
| 107 * assigned. | |
| 108 * (2) The number of kernel elements must be less than 2^29. | |
| 109 * </pre> | |
| 110 */ | |
| 111 L_KERNEL * | |
| 112 kernelCreate(l_int32 height, | |
| 113 l_int32 width) | |
| 114 { | |
| 115 l_uint64 size64; | |
| 116 L_KERNEL *kel; | |
| 117 | |
| 118 if (width <= 0) | |
| 119 return (L_KERNEL *)ERROR_PTR("width must be > 0", __func__, NULL); | |
| 120 if (height <= 0) | |
| 121 return (L_KERNEL *)ERROR_PTR("height must be > 0", __func__, NULL); | |
| 122 | |
| 123 /* Avoid overflow in malloc arg */ | |
| 124 size64 = (l_uint64)width * (l_uint64)height; | |
| 125 if (size64 >= (1LL << 29)) { | |
| 126 L_ERROR("requested width = %d, height = %d\n", __func__, width, height); | |
| 127 return (L_KERNEL *)ERROR_PTR("size >= 2^29", __func__, NULL); | |
| 128 } | |
| 129 | |
| 130 kel = (L_KERNEL *)LEPT_CALLOC(1, sizeof(L_KERNEL)); | |
| 131 kel->sy = height; | |
| 132 kel->sx = width; | |
| 133 if ((kel->data = create2dFloatArray(height, width)) == NULL) { | |
| 134 LEPT_FREE(kel); | |
| 135 return (L_KERNEL *)ERROR_PTR("data not allocated", __func__, NULL); | |
| 136 } | |
| 137 return kel; | |
| 138 } | |
| 139 | |
| 140 | |
| 141 /*! | |
| 142 * \brief kernelDestroy() | |
| 143 * | |
| 144 * \param[in,out] pkel will be set to null before returning | |
| 145 * \return void | |
| 146 */ | |
| 147 void | |
| 148 kernelDestroy(L_KERNEL **pkel) | |
| 149 { | |
| 150 l_int32 i; | |
| 151 L_KERNEL *kel; | |
| 152 | |
| 153 if (pkel == NULL) { | |
| 154 L_WARNING("ptr address is NULL!\n", __func__); | |
| 155 return; | |
| 156 } | |
| 157 if ((kel = *pkel) == NULL) | |
| 158 return; | |
| 159 | |
| 160 for (i = 0; i < kel->sy; i++) | |
| 161 LEPT_FREE(kel->data[i]); | |
| 162 LEPT_FREE(kel->data); | |
| 163 LEPT_FREE(kel); | |
| 164 *pkel = NULL; | |
| 165 } | |
| 166 | |
| 167 | |
| 168 /*! | |
| 169 * \brief kernelCopy() | |
| 170 * | |
| 171 * \param[in] kels source kernel | |
| 172 * \return keld copy of kels, or NULL on error | |
| 173 */ | |
| 174 L_KERNEL * | |
| 175 kernelCopy(L_KERNEL *kels) | |
| 176 { | |
| 177 l_int32 i, j, sx, sy, cx, cy; | |
| 178 L_KERNEL *keld; | |
| 179 | |
| 180 if (!kels) | |
| 181 return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL); | |
| 182 | |
| 183 kernelGetParameters(kels, &sy, &sx, &cy, &cx); | |
| 184 if ((keld = kernelCreate(sy, sx)) == NULL) | |
| 185 return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL); | |
| 186 keld->cy = cy; | |
| 187 keld->cx = cx; | |
| 188 for (i = 0; i < sy; i++) | |
| 189 for (j = 0; j < sx; j++) | |
| 190 keld->data[i][j] = kels->data[i][j]; | |
| 191 | |
| 192 return keld; | |
| 193 } | |
| 194 | |
| 195 | |
| 196 /*----------------------------------------------------------------------* | |
| 197 * Accessors * | |
| 198 *----------------------------------------------------------------------*/ | |
| 199 /*! | |
| 200 * \brief kernelGetElement() | |
| 201 * | |
| 202 * \param[in] kel | |
| 203 * \param[in] row | |
| 204 * \param[in] col | |
| 205 * \param[out] pval | |
| 206 * \return 0 if OK; 1 on error | |
| 207 */ | |
| 208 l_ok | |
| 209 kernelGetElement(L_KERNEL *kel, | |
| 210 l_int32 row, | |
| 211 l_int32 col, | |
| 212 l_float32 *pval) | |
| 213 { | |
| 214 if (!pval) | |
| 215 return ERROR_INT("&val not defined", __func__, 1); | |
| 216 *pval = 0; | |
| 217 if (!kel) | |
| 218 return ERROR_INT("kernel not defined", __func__, 1); | |
| 219 if (row < 0 || row >= kel->sy) | |
| 220 return ERROR_INT("kernel row out of bounds", __func__, 1); | |
| 221 if (col < 0 || col >= kel->sx) | |
| 222 return ERROR_INT("kernel col out of bounds", __func__, 1); | |
| 223 | |
| 224 *pval = kel->data[row][col]; | |
| 225 return 0; | |
| 226 } | |
| 227 | |
| 228 | |
| 229 /*! | |
| 230 * \brief kernelSetElement() | |
| 231 * | |
| 232 * \param[in] kel kernel | |
| 233 * \param[in] row | |
| 234 * \param[in] col | |
| 235 * \param[in] val | |
| 236 * \return 0 if OK; 1 on error | |
| 237 */ | |
| 238 l_ok | |
| 239 kernelSetElement(L_KERNEL *kel, | |
| 240 l_int32 row, | |
| 241 l_int32 col, | |
| 242 l_float32 val) | |
| 243 { | |
| 244 if (!kel) | |
| 245 return ERROR_INT("kel not defined", __func__, 1); | |
| 246 if (row < 0 || row >= kel->sy) | |
| 247 return ERROR_INT("kernel row out of bounds", __func__, 1); | |
| 248 if (col < 0 || col >= kel->sx) | |
| 249 return ERROR_INT("kernel col out of bounds", __func__, 1); | |
| 250 | |
| 251 kel->data[row][col] = val; | |
| 252 return 0; | |
| 253 } | |
| 254 | |
| 255 | |
| 256 /*! | |
| 257 * \brief kernelGetParameters() | |
| 258 * | |
| 259 * \param[in] kel kernel | |
| 260 * \param[out] psy, psx, pcy, pcx [optional] each can be null | |
| 261 * \return 0 if OK, 1 on error | |
| 262 */ | |
| 263 l_ok | |
| 264 kernelGetParameters(L_KERNEL *kel, | |
| 265 l_int32 *psy, | |
| 266 l_int32 *psx, | |
| 267 l_int32 *pcy, | |
| 268 l_int32 *pcx) | |
| 269 { | |
| 270 if (psy) *psy = 0; | |
| 271 if (psx) *psx = 0; | |
| 272 if (pcy) *pcy = 0; | |
| 273 if (pcx) *pcx = 0; | |
| 274 if (!kel) | |
| 275 return ERROR_INT("kernel not defined", __func__, 1); | |
| 276 if (psy) *psy = kel->sy; | |
| 277 if (psx) *psx = kel->sx; | |
| 278 if (pcy) *pcy = kel->cy; | |
| 279 if (pcx) *pcx = kel->cx; | |
| 280 return 0; | |
| 281 } | |
| 282 | |
| 283 | |
| 284 /*! | |
| 285 * \brief kernelSetOrigin() | |
| 286 * | |
| 287 * \param[in] kel kernel | |
| 288 * \param[in] cy, cx | |
| 289 * \return 0 if OK; 1 on error | |
| 290 */ | |
| 291 l_ok | |
| 292 kernelSetOrigin(L_KERNEL *kel, | |
| 293 l_int32 cy, | |
| 294 l_int32 cx) | |
| 295 { | |
| 296 if (!kel) | |
| 297 return ERROR_INT("kel not defined", __func__, 1); | |
| 298 kel->cy = cy; | |
| 299 kel->cx = cx; | |
| 300 return 0; | |
| 301 } | |
| 302 | |
| 303 | |
| 304 /*! | |
| 305 * \brief kernelGetSum() | |
| 306 * | |
| 307 * \param[in] kel kernel | |
| 308 * \param[out] psum sum of all kernel values | |
| 309 * \return 0 if OK, 1 on error | |
| 310 */ | |
| 311 l_ok | |
| 312 kernelGetSum(L_KERNEL *kel, | |
| 313 l_float32 *psum) | |
| 314 { | |
| 315 l_int32 sx, sy, i, j; | |
| 316 | |
| 317 if (!psum) | |
| 318 return ERROR_INT("&sum not defined", __func__, 1); | |
| 319 *psum = 0.0; | |
| 320 if (!kel) | |
| 321 return ERROR_INT("kernel not defined", __func__, 1); | |
| 322 | |
| 323 kernelGetParameters(kel, &sy, &sx, NULL, NULL); | |
| 324 for (i = 0; i < sy; i++) { | |
| 325 for (j = 0; j < sx; j++) { | |
| 326 *psum += kel->data[i][j]; | |
| 327 } | |
| 328 } | |
| 329 return 0; | |
| 330 } | |
| 331 | |
| 332 | |
| 333 /*! | |
| 334 * \brief kernelGetMinMax() | |
| 335 * | |
| 336 * \param[in] kel kernel | |
| 337 * \param[out] pmin [optional] minimum value | |
| 338 * \param[out] pmax [optional] maximum value | |
| 339 * \return 0 if OK, 1 on error | |
| 340 */ | |
| 341 l_ok | |
| 342 kernelGetMinMax(L_KERNEL *kel, | |
| 343 l_float32 *pmin, | |
| 344 l_float32 *pmax) | |
| 345 { | |
| 346 l_int32 sx, sy, i, j; | |
| 347 l_float32 val, minval, maxval; | |
| 348 | |
| 349 if (!pmin && !pmax) | |
| 350 return ERROR_INT("neither &min nor &max defined", __func__, 1); | |
| 351 if (pmin) *pmin = 0.0; | |
| 352 if (pmax) *pmax = 0.0; | |
| 353 if (!kel) | |
| 354 return ERROR_INT("kernel not defined", __func__, 1); | |
| 355 | |
| 356 kernelGetParameters(kel, &sy, &sx, NULL, NULL); | |
| 357 minval = 10000000.0; | |
| 358 maxval = -10000000.0; | |
| 359 for (i = 0; i < sy; i++) { | |
| 360 for (j = 0; j < sx; j++) { | |
| 361 val = kel->data[i][j]; | |
| 362 if (val < minval) | |
| 363 minval = val; | |
| 364 if (val > maxval) | |
| 365 maxval = val; | |
| 366 } | |
| 367 } | |
| 368 if (pmin) | |
| 369 *pmin = minval; | |
| 370 if (pmax) | |
| 371 *pmax = maxval; | |
| 372 | |
| 373 return 0; | |
| 374 } | |
| 375 | |
| 376 | |
| 377 /*----------------------------------------------------------------------* | |
| 378 * Normalize/Invert * | |
| 379 *----------------------------------------------------------------------*/ | |
| 380 /*! | |
| 381 * \brief kernelNormalize() | |
| 382 * | |
| 383 * \param[in] kels source kel, to be normalized | |
| 384 * \param[in] normsum desired sum of elements in keld | |
| 385 * \return keld normalized version of kels, or NULL on error | |
| 386 * or if sum of elements is very close to 0) | |
| 387 * | |
| 388 * <pre> | |
| 389 * Notes: | |
| 390 * (1) If the sum of kernel elements is close to 0, do not | |
| 391 * try to calculate the normalized kernel. Instead, | |
| 392 * return a copy of the input kernel, with a warning. | |
| 393 * </pre> | |
| 394 */ | |
| 395 L_KERNEL * | |
| 396 kernelNormalize(L_KERNEL *kels, | |
| 397 l_float32 normsum) | |
| 398 { | |
| 399 l_int32 i, j, sx, sy, cx, cy; | |
| 400 l_float32 sum, factor; | |
| 401 L_KERNEL *keld; | |
| 402 | |
| 403 if (!kels) | |
| 404 return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL); | |
| 405 | |
| 406 kernelGetSum(kels, &sum); | |
| 407 if (L_ABS(sum) < 0.00001) { | |
| 408 L_WARNING("null sum; not normalizing; returning a copy\n", __func__); | |
| 409 return kernelCopy(kels); | |
| 410 } | |
| 411 | |
| 412 kernelGetParameters(kels, &sy, &sx, &cy, &cx); | |
| 413 if ((keld = kernelCreate(sy, sx)) == NULL) | |
| 414 return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL); | |
| 415 keld->cy = cy; | |
| 416 keld->cx = cx; | |
| 417 | |
| 418 factor = normsum / sum; | |
| 419 for (i = 0; i < sy; i++) | |
| 420 for (j = 0; j < sx; j++) | |
| 421 keld->data[i][j] = factor * kels->data[i][j]; | |
| 422 | |
| 423 return keld; | |
| 424 } | |
| 425 | |
| 426 | |
| 427 /*! | |
| 428 * \brief kernelInvert() | |
| 429 * | |
| 430 * \param[in] kels source kel, to be inverted | |
| 431 * \return keld spatially inverted, about the origin, or NULL on error | |
| 432 * | |
| 433 * <pre> | |
| 434 * Notes: | |
| 435 * (1) For convolution, the kernel is spatially inverted before | |
| 436 * a "correlation" operation is done between the kernel and the image. | |
| 437 * </pre> | |
| 438 */ | |
| 439 L_KERNEL * | |
| 440 kernelInvert(L_KERNEL *kels) | |
| 441 { | |
| 442 l_int32 i, j, sx, sy, cx, cy; | |
| 443 L_KERNEL *keld; | |
| 444 | |
| 445 if (!kels) | |
| 446 return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL); | |
| 447 | |
| 448 kernelGetParameters(kels, &sy, &sx, &cy, &cx); | |
| 449 if ((keld = kernelCreate(sy, sx)) == NULL) | |
| 450 return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL); | |
| 451 keld->cy = sy - 1 - cy; | |
| 452 keld->cx = sx - 1 - cx; | |
| 453 | |
| 454 for (i = 0; i < sy; i++) | |
| 455 for (j = 0; j < sx; j++) | |
| 456 keld->data[i][j] = kels->data[sy - 1 - i][sx - 1 - j]; | |
| 457 | |
| 458 return keld; | |
| 459 } | |
| 460 | |
| 461 | |
| 462 /*----------------------------------------------------------------------* | |
| 463 * Helper function * | |
| 464 *----------------------------------------------------------------------*/ | |
| 465 /*! | |
| 466 * \brief create2dFloatArray() | |
| 467 * | |
| 468 * \param[in] sy rows == height | |
| 469 * \param[in] sx columns == width | |
| 470 * \return doubly indexed array i.e., an array of sy row pointers, | |
| 471 * each of which points to an array of sx floats | |
| 472 * | |
| 473 * <pre> | |
| 474 * Notes: | |
| 475 * (1) The array[%sy][%sx] is indexed in standard "matrix notation", | |
| 476 * with the row index first. | |
| 477 * (2) The caller kernelCreate() limits the size to < 2^29 pixels. | |
| 478 * </pre> | |
| 479 */ | |
| 480 l_float32 ** | |
| 481 create2dFloatArray(l_int32 sy, | |
| 482 l_int32 sx) | |
| 483 { | |
| 484 l_int32 i; | |
| 485 l_float32 **array; | |
| 486 | |
| 487 if (sx <= 0 || sx > MaxArraySize) | |
| 488 return (l_float32 **)ERROR_PTR("sx out of bounds", __func__, NULL); | |
| 489 if (sy <= 0 || sy > MaxArraySize) | |
| 490 return (l_float32 **)ERROR_PTR("sy out of bounds", __func__, NULL); | |
| 491 | |
| 492 array = (l_float32 **)LEPT_CALLOC(sy, sizeof(l_float32 *)); | |
| 493 for (i = 0; i < sy; i++) | |
| 494 array[i] = (l_float32 *)LEPT_CALLOC(sx, sizeof(l_float32)); | |
| 495 return array; | |
| 496 } | |
| 497 | |
| 498 | |
| 499 /*----------------------------------------------------------------------* | |
| 500 * Kernel serialized I/O * | |
| 501 *----------------------------------------------------------------------*/ | |
| 502 /*! | |
| 503 * \brief kernelRead() | |
| 504 * | |
| 505 * \param[in] fname filename | |
| 506 * \return kernel, or NULL on error | |
| 507 */ | |
| 508 L_KERNEL * | |
| 509 kernelRead(const char *fname) | |
| 510 { | |
| 511 FILE *fp; | |
| 512 L_KERNEL *kel; | |
| 513 | |
| 514 if (!fname) | |
| 515 return (L_KERNEL *)ERROR_PTR("fname not defined", __func__, NULL); | |
| 516 | |
| 517 if ((fp = fopenReadStream(fname)) == NULL) | |
| 518 return (L_KERNEL *)ERROR_PTR_1("stream not opened", | |
| 519 fname, __func__, NULL); | |
| 520 if ((kel = kernelReadStream(fp)) == NULL) { | |
| 521 fclose(fp); | |
| 522 return (L_KERNEL *)ERROR_PTR_1("kel not returned", | |
| 523 fname, __func__, NULL); | |
| 524 } | |
| 525 fclose(fp); | |
| 526 | |
| 527 return kel; | |
| 528 } | |
| 529 | |
| 530 | |
| 531 /*! | |
| 532 * \brief kernelReadStream() | |
| 533 * | |
| 534 * \param[in] fp file stream | |
| 535 * \return kernel, or NULL on error | |
| 536 */ | |
| 537 L_KERNEL * | |
| 538 kernelReadStream(FILE *fp) | |
| 539 { | |
| 540 l_int32 sy, sx, cy, cx, i, j, ret, version, ignore; | |
| 541 L_KERNEL *kel; | |
| 542 | |
| 543 if (!fp) | |
| 544 return (L_KERNEL *)ERROR_PTR("stream not defined", __func__, NULL); | |
| 545 | |
| 546 ret = fscanf(fp, " Kernel Version %d\n", &version); | |
| 547 if (ret != 1) | |
| 548 return (L_KERNEL *)ERROR_PTR("not a kernel file", __func__, NULL); | |
| 549 if (version != KERNEL_VERSION_NUMBER) | |
| 550 return (L_KERNEL *)ERROR_PTR("invalid kernel version", __func__, NULL); | |
| 551 | |
| 552 if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", | |
| 553 &sy, &sx, &cy, &cx) != 4) | |
| 554 return (L_KERNEL *)ERROR_PTR("dimensions not read", __func__, NULL); | |
| 555 if (sx > MaxArraySize || sy > MaxArraySize) { | |
| 556 L_ERROR("sx = %d or sy = %d > %d\n", __func__, sx, sy, MaxArraySize); | |
| 557 return NULL; | |
| 558 } | |
| 559 if ((kel = kernelCreate(sy, sx)) == NULL) | |
| 560 return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL); | |
| 561 kernelSetOrigin(kel, cy, cx); | |
| 562 | |
| 563 for (i = 0; i < sy; i++) { | |
| 564 for (j = 0; j < sx; j++) | |
| 565 ignore = fscanf(fp, "%15f", &kel->data[i][j]); | |
| 566 ignore = fscanf(fp, "\n"); | |
| 567 } | |
| 568 ignore = fscanf(fp, "\n"); | |
| 569 | |
| 570 return kel; | |
| 571 } | |
| 572 | |
| 573 | |
| 574 /*! | |
| 575 * \brief kernelWrite() | |
| 576 * | |
| 577 * \param[in] fname output file | |
| 578 * \param[in] kel kernel | |
| 579 * \return 0 if OK, 1 on error | |
| 580 */ | |
| 581 l_ok | |
| 582 kernelWrite(const char *fname, | |
| 583 L_KERNEL *kel) | |
| 584 { | |
| 585 FILE *fp; | |
| 586 | |
| 587 if (!fname) | |
| 588 return ERROR_INT("fname not defined", __func__, 1); | |
| 589 if (!kel) | |
| 590 return ERROR_INT("kel not defined", __func__, 1); | |
| 591 | |
| 592 if ((fp = fopenWriteStream(fname, "wb")) == NULL) | |
| 593 return ERROR_INT_1("stream not opened", fname, __func__, 1); | |
| 594 kernelWriteStream(fp, kel); | |
| 595 fclose(fp); | |
| 596 | |
| 597 return 0; | |
| 598 } | |
| 599 | |
| 600 | |
| 601 /*! | |
| 602 * \brief kernelWriteStream() | |
| 603 * | |
| 604 * \param[in] fp file stream | |
| 605 * \param[in] kel | |
| 606 * \return 0 if OK, 1 on error | |
| 607 */ | |
| 608 l_ok | |
| 609 kernelWriteStream(FILE *fp, | |
| 610 L_KERNEL *kel) | |
| 611 { | |
| 612 l_int32 sx, sy, cx, cy, i, j; | |
| 613 | |
| 614 if (!fp) | |
| 615 return ERROR_INT("stream not defined", __func__, 1); | |
| 616 if (!kel) | |
| 617 return ERROR_INT("kel not defined", __func__, 1); | |
| 618 kernelGetParameters(kel, &sy, &sx, &cy, &cx); | |
| 619 | |
| 620 fprintf(fp, " Kernel Version %d\n", KERNEL_VERSION_NUMBER); | |
| 621 fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); | |
| 622 for (i = 0; i < sy; i++) { | |
| 623 for (j = 0; j < sx; j++) | |
| 624 fprintf(fp, "%15.4f", kel->data[i][j]); | |
| 625 fprintf(fp, "\n"); | |
| 626 } | |
| 627 fprintf(fp, "\n"); | |
| 628 | |
| 629 return 0; | |
| 630 } | |
| 631 | |
| 632 | |
| 633 /*----------------------------------------------------------------------* | |
| 634 * Making a kernel from a compiled string * | |
| 635 *----------------------------------------------------------------------*/ | |
| 636 /*! | |
| 637 * \brief kernelCreateFromString() | |
| 638 * | |
| 639 * \param[in] h, w height, width | |
| 640 * \param[in] cy, cx origin | |
| 641 * \param[in] kdata | |
| 642 * \return kernel of the given size, or NULL on error | |
| 643 * | |
| 644 * <pre> | |
| 645 * Notes: | |
| 646 * (1) The data is an array of chars, in row-major order, giving | |
| 647 * space separated integers in the range [-255 ... 255]. | |
| 648 * (2) The only other formatting limitation is that you must | |
| 649 * leave space between the last number in each row and | |
| 650 * the double-quote. If possible, it's also nice to have each | |
| 651 * line in the string represent a line in the kernel; e.g., | |
| 652 * static const char *kdata = | |
| 653 * " 20 50 20 " | |
| 654 * " 70 140 70 " | |
| 655 * " 20 50 20 "; | |
| 656 * </pre> | |
| 657 */ | |
| 658 L_KERNEL * | |
| 659 kernelCreateFromString(l_int32 h, | |
| 660 l_int32 w, | |
| 661 l_int32 cy, | |
| 662 l_int32 cx, | |
| 663 const char *kdata) | |
| 664 { | |
| 665 l_int32 n, i, j, index; | |
| 666 l_float32 val; | |
| 667 L_KERNEL *kel; | |
| 668 NUMA *na; | |
| 669 | |
| 670 if (h < 1) | |
| 671 return (L_KERNEL *)ERROR_PTR("height must be > 0", __func__, NULL); | |
| 672 if (w < 1) | |
| 673 return (L_KERNEL *)ERROR_PTR("width must be > 0", __func__, NULL); | |
| 674 if (cy < 0 || cy >= h) | |
| 675 return (L_KERNEL *)ERROR_PTR("cy invalid", __func__, NULL); | |
| 676 if (cx < 0 || cx >= w) | |
| 677 return (L_KERNEL *)ERROR_PTR("cx invalid", __func__, NULL); | |
| 678 | |
| 679 kel = kernelCreate(h, w); | |
| 680 kernelSetOrigin(kel, cy, cx); | |
| 681 na = parseStringForNumbers(kdata, " \t\n"); | |
| 682 n = numaGetCount(na); | |
| 683 if (n != w * h) { | |
| 684 kernelDestroy(&kel); | |
| 685 numaDestroy(&na); | |
| 686 lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n); | |
| 687 return (L_KERNEL *)ERROR_PTR("invalid integer data", __func__, NULL); | |
| 688 } | |
| 689 | |
| 690 index = 0; | |
| 691 for (i = 0; i < h; i++) { | |
| 692 for (j = 0; j < w; j++) { | |
| 693 numaGetFValue(na, index, &val); | |
| 694 kernelSetElement(kel, i, j, val); | |
| 695 index++; | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 numaDestroy(&na); | |
| 700 return kel; | |
| 701 } | |
| 702 | |
| 703 | |
| 704 /*----------------------------------------------------------------------* | |
| 705 * Making a kernel from a simple file format * | |
| 706 *----------------------------------------------------------------------*/ | |
| 707 /*! | |
| 708 * \brief kernelCreateFromFile() | |
| 709 * | |
| 710 * \param[in] filename | |
| 711 * \return kernel, or NULL on error | |
| 712 * | |
| 713 * <pre> | |
| 714 * Notes: | |
| 715 * (1) The file contains, in the following order: | |
| 716 * ~ Any number of comment lines starting with '#' are ignored | |
| 717 * ~ The height and width of the kernel | |
| 718 * ~ The y and x values of the kernel origin | |
| 719 * ~ The kernel data, formatted as lines of numbers (integers | |
| 720 * or floats) for the kernel values in row-major order, | |
| 721 * and with no other punctuation. | |
| 722 * (Note: this differs from kernelCreateFromString(), | |
| 723 * where each line must begin and end with a double-quote | |
| 724 * to tell the compiler it's part of a string.) | |
| 725 * ~ The kernel specification ends when a blank line, | |
| 726 * a comment line, or the end of file is reached. | |
| 727 * (2) All lines must be left-justified. | |
| 728 * (3) See kernelCreateFromString() for a description of the string | |
| 729 * format for the kernel data. As an example, here are the lines | |
| 730 * of a valid kernel description file In the file, all lines | |
| 731 * are left-justified: | |
| 732 * \code | |
| 733 * # small 3x3 kernel | |
| 734 * 3 3 | |
| 735 * 1 1 | |
| 736 * 25.5 51 24.3 | |
| 737 * 70.2 146.3 73.4 | |
| 738 * 20 50.9 18.4 | |
| 739 * \endcode | |
| 740 * </pre> | |
| 741 */ | |
| 742 L_KERNEL * | |
| 743 kernelCreateFromFile(const char *filename) | |
| 744 { | |
| 745 char *filestr, *line; | |
| 746 l_int32 nlines, i, j, first, index, w, h, cx, cy, n; | |
| 747 l_float32 val; | |
| 748 size_t size; | |
| 749 NUMA *na, *nat; | |
| 750 SARRAY *sa; | |
| 751 L_KERNEL *kel; | |
| 752 | |
| 753 if (!filename) | |
| 754 return (L_KERNEL *)ERROR_PTR("filename not defined", __func__, NULL); | |
| 755 | |
| 756 if ((filestr = (char *)l_binaryRead(filename, &size)) == NULL) | |
| 757 return (L_KERNEL *)ERROR_PTR_1("file not found", | |
| 758 filename, __func__, NULL); | |
| 759 if (size == 0) { | |
| 760 LEPT_FREE(filestr); | |
| 761 return (L_KERNEL *)ERROR_PTR_1("file is empty", | |
| 762 filename, __func__, NULL); | |
| 763 } | |
| 764 | |
| 765 sa = sarrayCreateLinesFromString(filestr, 1); | |
| 766 LEPT_FREE(filestr); | |
| 767 nlines = sarrayGetCount(sa); | |
| 768 | |
| 769 /* Find the first data line. */ | |
| 770 for (i = 0, first = 0; i < nlines; i++) { | |
| 771 line = sarrayGetString(sa, i, L_NOCOPY); | |
| 772 if (line[0] != '#') { | |
| 773 first = i; | |
| 774 break; | |
| 775 } | |
| 776 } | |
| 777 | |
| 778 /* Find the kernel dimensions and origin location. */ | |
| 779 line = sarrayGetString(sa, first, L_NOCOPY); | |
| 780 if (sscanf(line, "%d %d", &h, &w) != 2) { | |
| 781 sarrayDestroy(&sa); | |
| 782 return (L_KERNEL *)ERROR_PTR("error reading h,w", __func__, NULL); | |
| 783 } | |
| 784 if (h > MaxArraySize || w > MaxArraySize) { | |
| 785 L_ERROR("h = %d or w = %d > %d\n", __func__, h, w, MaxArraySize); | |
| 786 sarrayDestroy(&sa); | |
| 787 return NULL; | |
| 788 } | |
| 789 line = sarrayGetString(sa, first + 1, L_NOCOPY); | |
| 790 if (sscanf(line, "%d %d", &cy, &cx) != 2) { | |
| 791 sarrayDestroy(&sa); | |
| 792 return (L_KERNEL *)ERROR_PTR("error reading cy,cx", __func__, NULL); | |
| 793 } | |
| 794 | |
| 795 /* Extract the data. This ends when we reach eof, or when we | |
| 796 * encounter a line of data that is either a null string or | |
| 797 * contains just a newline. */ | |
| 798 na = numaCreate(0); | |
| 799 for (i = first + 2; i < nlines; i++) { | |
| 800 line = sarrayGetString(sa, i, L_NOCOPY); | |
| 801 if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') | |
| 802 break; | |
| 803 nat = parseStringForNumbers(line, " \t\n"); | |
| 804 numaJoin(na, nat, 0, -1); | |
| 805 numaDestroy(&nat); | |
| 806 } | |
| 807 sarrayDestroy(&sa); | |
| 808 | |
| 809 n = numaGetCount(na); | |
| 810 if (n != w * h) { | |
| 811 numaDestroy(&na); | |
| 812 lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n); | |
| 813 return (L_KERNEL *)ERROR_PTR("invalid integer data", __func__, NULL); | |
| 814 } | |
| 815 | |
| 816 kel = kernelCreate(h, w); | |
| 817 kernelSetOrigin(kel, cy, cx); | |
| 818 index = 0; | |
| 819 for (i = 0; i < h; i++) { | |
| 820 for (j = 0; j < w; j++) { | |
| 821 numaGetFValue(na, index, &val); | |
| 822 kernelSetElement(kel, i, j, val); | |
| 823 index++; | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 numaDestroy(&na); | |
| 828 return kel; | |
| 829 } | |
| 830 | |
| 831 | |
| 832 /*----------------------------------------------------------------------* | |
| 833 * Making a kernel from a Pix * | |
| 834 *----------------------------------------------------------------------*/ | |
| 835 /*! | |
| 836 * \brief kernelCreateFromPix() | |
| 837 * | |
| 838 * \param[in] pix | |
| 839 * \param[in] cy, cx origin of kernel | |
| 840 * \return kernel, or NULL on error | |
| 841 * | |
| 842 * <pre> | |
| 843 * Notes: | |
| 844 * (1) The origin must be positive and within the dimensions of the pix. | |
| 845 * </pre> | |
| 846 */ | |
| 847 L_KERNEL * | |
| 848 kernelCreateFromPix(PIX *pix, | |
| 849 l_int32 cy, | |
| 850 l_int32 cx) | |
| 851 { | |
| 852 l_int32 i, j, w, h, d; | |
| 853 l_uint32 val; | |
| 854 L_KERNEL *kel; | |
| 855 | |
| 856 if (!pix) | |
| 857 return (L_KERNEL *)ERROR_PTR("pix not defined", __func__, NULL); | |
| 858 pixGetDimensions(pix, &w, &h, &d); | |
| 859 if (d != 8) | |
| 860 return (L_KERNEL *)ERROR_PTR("pix not 8 bpp", __func__, NULL); | |
| 861 if (cy < 0 || cx < 0 || cy >= h || cx >= w) | |
| 862 return (L_KERNEL *)ERROR_PTR("(cy, cx) invalid", __func__, NULL); | |
| 863 | |
| 864 kel = kernelCreate(h, w); | |
| 865 kernelSetOrigin(kel, cy, cx); | |
| 866 for (i = 0; i < h; i++) { | |
| 867 for (j = 0; j < w; j++) { | |
| 868 pixGetPixel(pix, j, i, &val); | |
| 869 kernelSetElement(kel, i, j, (l_float32)val); | |
| 870 } | |
| 871 } | |
| 872 | |
| 873 return kel; | |
| 874 } | |
| 875 | |
| 876 | |
| 877 /*----------------------------------------------------------------------* | |
| 878 * Display a kernel in a pix * | |
| 879 *----------------------------------------------------------------------*/ | |
| 880 /*! | |
| 881 * \brief kernelDisplayInPix() | |
| 882 * | |
| 883 * \param[in] kel kernel | |
| 884 * \param[in] size of grid interiors; odd; either 1 or a minimum size | |
| 885 * of 17 is enforced | |
| 886 * \param[in] gthick grid thickness; either 0 or a minimum size of 2 | |
| 887 * is enforced | |
| 888 * \return pix display of kernel, or NULL on error | |
| 889 * | |
| 890 * <pre> | |
| 891 * Notes: | |
| 892 * (1) This gives a visual representation of a kernel. | |
| 893 * (2) There are two modes of display: | |
| 894 * (a) Grid lines of minimum width 2, surrounding regions | |
| 895 * representing kernel elements of minimum size 17, | |
| 896 * with a "plus" mark at the kernel origin, or | |
| 897 * (b) A pix without grid lines and using 1 pixel per kernel element. | |
| 898 * (3) For both cases, the kernel absolute value is displayed, | |
| 899 * normalized such that the maximum absolute value is 255. | |
| 900 * (4) Large 2D separable kernels should be used for convolution | |
| 901 * with two 1D kernels. However, for the bilateral filter, | |
| 902 * the computation time is independent of the size of the | |
| 903 * 2D content kernel. | |
| 904 * </pre> | |
| 905 */ | |
| 906 PIX * | |
| 907 kernelDisplayInPix(L_KERNEL *kel, | |
| 908 l_int32 size, | |
| 909 l_int32 gthick) | |
| 910 { | |
| 911 l_int32 i, j, w, h, sx, sy, cx, cy, width, x0, y0; | |
| 912 l_int32 normval; | |
| 913 l_float32 minval, maxval, max, val, norm; | |
| 914 PIX *pixd, *pixt0, *pixt1; | |
| 915 | |
| 916 if (!kel) | |
| 917 return (PIX *)ERROR_PTR("kernel not defined", __func__, NULL); | |
| 918 | |
| 919 /* Normalize the max value to be 255 for display */ | |
| 920 kernelGetParameters(kel, &sy, &sx, &cy, &cx); | |
| 921 kernelGetMinMax(kel, &minval, &maxval); | |
| 922 max = L_MAX(maxval, -minval); | |
| 923 if (max == 0.0) | |
| 924 return (PIX *)ERROR_PTR("kernel elements all 0.0", __func__, NULL); | |
| 925 norm = 255.f / (l_float32)max; | |
| 926 | |
| 927 /* Handle the 1 element/pixel case; typically with large kernels */ | |
| 928 if (size == 1 && gthick == 0) { | |
| 929 pixd = pixCreate(sx, sy, 8); | |
| 930 for (i = 0; i < sy; i++) { | |
| 931 for (j = 0; j < sx; j++) { | |
| 932 kernelGetElement(kel, i, j, &val); | |
| 933 normval = (l_int32)(norm * L_ABS(val)); | |
| 934 pixSetPixel(pixd, j, i, normval); | |
| 935 } | |
| 936 } | |
| 937 return pixd; | |
| 938 } | |
| 939 | |
| 940 /* Enforce the constraints for the grid line version */ | |
| 941 if (size < 17) { | |
| 942 L_WARNING("size < 17; setting to 17\n", __func__); | |
| 943 size = 17; | |
| 944 } | |
| 945 if (size % 2 == 0) | |
| 946 size++; | |
| 947 if (gthick < 2) { | |
| 948 L_WARNING("grid thickness < 2; setting to 2\n", __func__); | |
| 949 gthick = 2; | |
| 950 } | |
| 951 | |
| 952 w = size * sx + gthick * (sx + 1); | |
| 953 h = size * sy + gthick * (sy + 1); | |
| 954 pixd = pixCreate(w, h, 8); | |
| 955 | |
| 956 /* Generate grid lines */ | |
| 957 for (i = 0; i <= sy; i++) | |
| 958 pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), | |
| 959 w - 1, gthick / 2 + i * (size + gthick), | |
| 960 gthick, L_SET_PIXELS); | |
| 961 for (j = 0; j <= sx; j++) | |
| 962 pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, | |
| 963 gthick / 2 + j * (size + gthick), h - 1, | |
| 964 gthick, L_SET_PIXELS); | |
| 965 | |
| 966 /* Generate mask for each element */ | |
| 967 pixt0 = pixCreate(size, size, 1); | |
| 968 pixSetAll(pixt0); | |
| 969 | |
| 970 /* Generate crossed lines for origin pattern */ | |
| 971 pixt1 = pixCreate(size, size, 1); | |
| 972 width = size / 8; | |
| 973 pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size), | |
| 974 size / 2, (l_int32)(0.88 * size), | |
| 975 width, L_SET_PIXELS); | |
| 976 pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2, | |
| 977 (l_int32)(0.85 * size), size / 2, | |
| 978 width, L_FLIP_PIXELS); | |
| 979 pixRasterop(pixt1, size / 2 - width, size / 2 - width, | |
| 980 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); | |
| 981 | |
| 982 /* Paste the patterns in */ | |
| 983 y0 = gthick; | |
| 984 for (i = 0; i < sy; i++) { | |
| 985 x0 = gthick; | |
| 986 for (j = 0; j < sx; j++) { | |
| 987 kernelGetElement(kel, i, j, &val); | |
| 988 normval = (l_int32)(norm * L_ABS(val)); | |
| 989 pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0); | |
| 990 if (i == cy && j == cx) | |
| 991 pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval); | |
| 992 x0 += size + gthick; | |
| 993 } | |
| 994 y0 += size + gthick; | |
| 995 } | |
| 996 | |
| 997 pixDestroy(&pixt0); | |
| 998 pixDestroy(&pixt1); | |
| 999 return pixd; | |
| 1000 } | |
| 1001 | |
| 1002 | |
| 1003 /*------------------------------------------------------------------------* | |
| 1004 * Parse string to extract numbers * | |
| 1005 *------------------------------------------------------------------------*/ | |
| 1006 /*! | |
| 1007 * \brief parseStringForNumbers() | |
| 1008 * | |
| 1009 * \param[in] str string containing numbers; not changed | |
| 1010 * \param[in] seps string of characters that can be used between ints | |
| 1011 * \return numa of numbers found, or NULL on error | |
| 1012 * | |
| 1013 * <pre> | |
| 1014 * Notes: | |
| 1015 * (1) The numbers can be ints or floats. | |
| 1016 * </pre> | |
| 1017 */ | |
| 1018 NUMA * | |
| 1019 parseStringForNumbers(const char *str, | |
| 1020 const char *seps) | |
| 1021 { | |
| 1022 char *newstr, *head; | |
| 1023 char *tail = NULL; | |
| 1024 l_float32 val; | |
| 1025 NUMA *na; | |
| 1026 | |
| 1027 if (!str) | |
| 1028 return (NUMA *)ERROR_PTR("str not defined", __func__, NULL); | |
| 1029 | |
| 1030 newstr = stringNew(str); /* to enforce const-ness of str */ | |
| 1031 na = numaCreate(0); | |
| 1032 head = strtokSafe(newstr, seps, &tail); | |
| 1033 val = atof(head); | |
| 1034 numaAddNumber(na, val); | |
| 1035 LEPT_FREE(head); | |
| 1036 while ((head = strtokSafe(NULL, seps, &tail)) != NULL) { | |
| 1037 val = atof(head); | |
| 1038 numaAddNumber(na, val); | |
| 1039 LEPT_FREE(head); | |
| 1040 } | |
| 1041 | |
| 1042 LEPT_FREE(newstr); | |
| 1043 return na; | |
| 1044 } | |
| 1045 | |
| 1046 | |
| 1047 /*------------------------------------------------------------------------* | |
| 1048 * Simple parametric kernels * | |
| 1049 *------------------------------------------------------------------------*/ | |
| 1050 /*! | |
| 1051 * \brief makeFlatKernel() | |
| 1052 * | |
| 1053 * \param[in] height, width | |
| 1054 * \param[in] cy, cx origin of kernel | |
| 1055 * \return kernel, or NULL on error | |
| 1056 * | |
| 1057 * <pre> | |
| 1058 * Notes: | |
| 1059 * (1) This is the same low-pass filtering kernel that is used | |
| 1060 * in the block convolution functions. | |
| 1061 * (2) The kernel origin (%cy, %cx) is typically placed as near | |
| 1062 * the center of the kernel as possible. If height and | |
| 1063 * width are odd, then using %cy = height / 2 and | |
| 1064 * %cx = width / 2 places the origin at the exact center. | |
| 1065 * (3) This returns a normalized kernel. | |
| 1066 * </pre> | |
| 1067 */ | |
| 1068 L_KERNEL * | |
| 1069 makeFlatKernel(l_int32 height, | |
| 1070 l_int32 width, | |
| 1071 l_int32 cy, | |
| 1072 l_int32 cx) | |
| 1073 { | |
| 1074 l_int32 i, j; | |
| 1075 l_float32 normval; | |
| 1076 L_KERNEL *kel; | |
| 1077 | |
| 1078 if ((kel = kernelCreate(height, width)) == NULL) | |
| 1079 return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL); | |
| 1080 kernelSetOrigin(kel, cy, cx); | |
| 1081 normval = 1.0f / (l_float32)(height * width); | |
| 1082 for (i = 0; i < height; i++) { | |
| 1083 for (j = 0; j < width; j++) { | |
| 1084 kernelSetElement(kel, i, j, normval); | |
| 1085 } | |
| 1086 } | |
| 1087 | |
| 1088 return kel; | |
| 1089 } | |
| 1090 | |
| 1091 | |
| 1092 /*! | |
| 1093 * \brief makeGaussianKernel() | |
| 1094 * | |
| 1095 * \param[in] halfh sy = 2 * halfh + 1 | |
| 1096 * \param[in] halfw sx = 2 * halfw + 1 | |
| 1097 * \param[in] stdev standard deviation | |
| 1098 * \param[in] max value at (cx,cy) | |
| 1099 * \return kernel, or NULL on error | |
| 1100 * | |
| 1101 * <pre> | |
| 1102 * Notes: | |
| 1103 * (1) The kernel size (sx, sy) = (2 * %halfw + 1, 2 * %halfh + 1) | |
| 1104 * (2) The kernel center (cx, cy) = (%halfw, %halfh). | |
| 1105 * (3) %halfw and %halfh are typically equal, and | |
| 1106 * are typically several times larger than the standard deviation. | |
| 1107 * (4) If pixConvolve() is invoked with normalization (the sum of | |
| 1108 * kernel elements = 1.0), use 1.0 for max (or any number that's | |
| 1109 * not too small or too large). | |
| 1110 * </pre> | |
| 1111 */ | |
| 1112 L_KERNEL * | |
| 1113 makeGaussianKernel(l_int32 halfh, | |
| 1114 l_int32 halfw, | |
| 1115 l_float32 stdev, | |
| 1116 l_float32 max) | |
| 1117 { | |
| 1118 l_int32 sx, sy, i, j; | |
| 1119 l_float32 val; | |
| 1120 L_KERNEL *kel; | |
| 1121 | |
| 1122 sx = 2 * halfw + 1; | |
| 1123 sy = 2 * halfh + 1; | |
| 1124 if ((kel = kernelCreate(sy, sx)) == NULL) | |
| 1125 return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL); | |
| 1126 kernelSetOrigin(kel, halfh, halfw); | |
| 1127 for (i = 0; i < sy; i++) { | |
| 1128 for (j = 0; j < sx; j++) { | |
| 1129 val = expf(-(l_float32)((i - halfh) * (i - halfh) + | |
| 1130 (j - halfw) * (j - halfw)) / | |
| 1131 (2. * stdev * stdev)); | |
| 1132 kernelSetElement(kel, i, j, max * val); | |
| 1133 } | |
| 1134 } | |
| 1135 | |
| 1136 return kel; | |
| 1137 } | |
| 1138 | |
| 1139 | |
| 1140 /*! | |
| 1141 * \brief makeGaussianKernelSep() | |
| 1142 * | |
| 1143 * \param[in] halfh sy = 2 * halfh + 1 | |
| 1144 * \param[in] halfw sx = 2 * halfw + 1 | |
| 1145 * \param[in] stdev standard deviation | |
| 1146 * \param[in] max value at (cx,cy) | |
| 1147 * \param[out] pkelx x part of kernel | |
| 1148 * \param[out] pkely y part of kernel | |
| 1149 * \return 0 if OK, 1 on error | |
| 1150 * | |
| 1151 * <pre> | |
| 1152 * Notes: | |
| 1153 * (1) See makeGaussianKernel() for description of input parameters. | |
| 1154 * (2) These kernels are constructed so that the result of both | |
| 1155 * normalized and un-normalized convolution will be the same | |
| 1156 * as when convolving with pixConvolve() using the full kernel. | |
| 1157 * (3) The trick for the un-normalized convolution is to have the | |
| 1158 * product of the two kernel elements at (cx,cy) be equal to %max, | |
| 1159 * not max**2. That's why %max for kely is 1.0. If instead | |
| 1160 * we use sqrt(%max) for both, the results are slightly less | |
| 1161 * accurate, when compared to using the full kernel in | |
| 1162 * makeGaussianKernel(). | |
| 1163 * </pre> | |
| 1164 */ | |
| 1165 l_ok | |
| 1166 makeGaussianKernelSep(l_int32 halfh, | |
| 1167 l_int32 halfw, | |
| 1168 l_float32 stdev, | |
| 1169 l_float32 max, | |
| 1170 L_KERNEL **pkelx, | |
| 1171 L_KERNEL **pkely) | |
| 1172 { | |
| 1173 if (!pkelx || !pkely) | |
| 1174 return ERROR_INT("&kelx and &kely not defined", __func__, 1); | |
| 1175 | |
| 1176 *pkelx = makeGaussianKernel(0, halfw, stdev, max); | |
| 1177 *pkely = makeGaussianKernel(halfh, 0, stdev, 1.0); | |
| 1178 return 0; | |
| 1179 } | |
| 1180 | |
| 1181 | |
| 1182 /*! | |
| 1183 * \brief makeDoGKernel() | |
| 1184 * | |
| 1185 * \param[in] halfh sy = 2 * halfh + 1 | |
| 1186 * \param[in] halfw sx = 2 * halfw + 1 | |
| 1187 * \param[in] stdev standard deviation of narrower gaussian | |
| 1188 * \param[in] ratio of stdev for wide filter to stdev for narrow one | |
| 1189 * \return kernel, or NULL on error | |
| 1190 * | |
| 1191 * <pre> | |
| 1192 * Notes: | |
| 1193 * (1) The DoG (difference of gaussians) is a wavelet mother | |
| 1194 * function with null total sum. By subtracting two blurred | |
| 1195 * versions of the image, it acts as a bandpass filter for | |
| 1196 * frequencies passed by the narrow gaussian but stopped | |
| 1197 * by the wide one.See: | |
| 1198 * http://en.wikipedia.org/wiki/Difference_of_Gaussians | |
| 1199 * (2) The kernel size (sx, sy) = (2 * halfw + 1, 2 * halfh + 1). | |
| 1200 * (3) The kernel center (cx, cy) = (halfw, halfh). | |
| 1201 * (4) %halfw and %halfh are typically equal, and are typically | |
| 1202 * several times larger than the standard deviation. | |
| 1203 * (5) %ratio is the ratio of standard deviations of the wide | |
| 1204 * to narrow gaussian. It must be >= 1.0; 1.0 is a no-op. | |
| 1205 * (6) Because the kernel is a null sum, it must be invoked without | |
| 1206 * normalization in pixConvolve(). | |
| 1207 * </pre> | |
| 1208 */ | |
| 1209 L_KERNEL * | |
| 1210 makeDoGKernel(l_int32 halfh, | |
| 1211 l_int32 halfw, | |
| 1212 l_float32 stdev, | |
| 1213 l_float32 ratio) | |
| 1214 { | |
| 1215 l_int32 sx, sy, i, j; | |
| 1216 l_float32 pi, squaredist, highnorm, lownorm, val; | |
| 1217 L_KERNEL *kel; | |
| 1218 | |
| 1219 sx = 2 * halfw + 1; | |
| 1220 sy = 2 * halfh + 1; | |
| 1221 if ((kel = kernelCreate(sy, sx)) == NULL) | |
| 1222 return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL); | |
| 1223 kernelSetOrigin(kel, halfh, halfw); | |
| 1224 | |
| 1225 pi = 3.1415926535f; | |
| 1226 for (i = 0; i < sy; i++) { | |
| 1227 for (j = 0; j < sx; j++) { | |
| 1228 squaredist = (l_float32)((i - halfh) * (i - halfh) + | |
| 1229 (j - halfw) * (j - halfw)); | |
| 1230 highnorm = 1.f / (2 * stdev * stdev); | |
| 1231 lownorm = highnorm / (ratio * ratio); | |
| 1232 val = (highnorm / pi) * expf(-(highnorm * squaredist)) | |
| 1233 - (lownorm / pi) * expf(-(lownorm * squaredist)); | |
| 1234 kernelSetElement(kel, i, j, val); | |
| 1235 } | |
| 1236 } | |
| 1237 | |
| 1238 return kel; | |
| 1239 } |
