Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 00018 /* 00019 * kernel.c 00020 * 00021 * Basic operations on kernels for image convolution 00022 * 00023 * Create/destroy/copy 00024 * L_KERNEL *kernelCreate() 00025 * void kernelDestroy() 00026 * L_KERNEL *kernelCopy() 00027 * 00028 * Accessors: 00029 * l_int32 kernelGetElement() 00030 * l_int32 kernelSetElement() 00031 * l_int32 kernelGetParameters() 00032 * l_int32 kernelSetOrigin() 00033 * l_int32 kernelGetSum() 00034 * l_int32 kernelGetMinMax() 00035 * 00036 * Normalize/invert 00037 * L_KERNEL *kernelNormalize() 00038 * L_KERNEL *kernelInvert() 00039 * 00040 * Helper function 00041 * l_float32 **create2dFloatArray() 00042 * 00043 * Serialized I/O 00044 * L_KERNEL *kernelRead() 00045 * L_KERNEL *kernelReadStream() 00046 * l_int32 kernelWrite() 00047 * l_int32 kernelWriteStream() 00048 * 00049 * Making a kernel from a compiled string 00050 * L_KERNEL *kernelCreateFromString() 00051 * 00052 * Making a kernel from a simple file format 00053 * L_KERNEL *kernelCreateFromFile() 00054 * 00055 * Making a kernel from a Pix 00056 * L_KERNEL *kernelCreateFromPix() 00057 * 00058 * Display a kernel in a pix 00059 * PIX *kernelDisplayInPix() 00060 * 00061 * Parse string to extract numbers 00062 * NUMA *parseStringForNumbers() 00063 * 00064 * Simple parametric kernels 00065 * L_KERNEL *makeFlatKernel() 00066 * L_KERNEL *makeGaussianKernel() 00067 * L_KERNEL *makeGaussianKernelSep() 00068 * L_KERNEL *makeDoGKernel() 00069 */ 00070 00071 #include <string.h> 00072 #include <math.h> 00073 #include "allheaders.h" 00074 00075 00076 /*------------------------------------------------------------------------* 00077 * Create / Destroy * 00078 *------------------------------------------------------------------------*/ 00079 /*! 00080 * kernelCreate() 00081 * 00082 * Input: height, width 00083 * Return: kernel, or null on error 00084 * 00085 * Notes: 00086 * (1) kernelCreate() initializes all values to 0. 00087 * (2) After this call, (cy,cx) and nonzero data values must be 00088 * assigned. 00089 */ 00090 L_KERNEL * 00091 kernelCreate(l_int32 height, 00092 l_int32 width) 00093 { 00094 L_KERNEL *kel; 00095 00096 PROCNAME("kernelCreate"); 00097 00098 if ((kel = (L_KERNEL *)CALLOC(1, sizeof(L_KERNEL))) == NULL) 00099 return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); 00100 kel->sy = height; 00101 kel->sx = width; 00102 if ((kel->data = create2dFloatArray(height, width)) == NULL) 00103 return (L_KERNEL *)ERROR_PTR("data not allocated", procName, NULL); 00104 00105 return kel; 00106 } 00107 00108 00109 /*! 00110 * kernelDestroy() 00111 * 00112 * Input: &kel (<to be nulled>) 00113 * Return: void 00114 */ 00115 void 00116 kernelDestroy(L_KERNEL **pkel) 00117 { 00118 l_int32 i; 00119 L_KERNEL *kel; 00120 00121 PROCNAME("kernelDestroy"); 00122 00123 if (pkel == NULL) { 00124 L_WARNING("ptr address is NULL!", procName); 00125 return; 00126 } 00127 if ((kel = *pkel) == NULL) 00128 return; 00129 00130 for (i = 0; i < kel->sy; i++) 00131 FREE(kel->data[i]); 00132 FREE(kel->data); 00133 FREE(kel); 00134 00135 *pkel = NULL; 00136 return; 00137 } 00138 00139 00140 /*! 00141 * kernelCopy() 00142 * 00143 * Input: kels (source kernel) 00144 * Return: keld (copy of kels), or null on error 00145 */ 00146 L_KERNEL * 00147 kernelCopy(L_KERNEL *kels) 00148 { 00149 l_int32 i, j, sx, sy, cx, cy; 00150 L_KERNEL *keld; 00151 00152 PROCNAME("kernelCopy"); 00153 00154 if (!kels) 00155 return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); 00156 00157 kernelGetParameters(kels, &sy, &sx, &cy, &cx); 00158 if ((keld = kernelCreate(sy, sx)) == NULL) 00159 return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); 00160 keld->cy = cy; 00161 keld->cx = cx; 00162 for (i = 0; i < sy; i++) 00163 for (j = 0; j < sx; j++) 00164 keld->data[i][j] = kels->data[i][j]; 00165 00166 return keld; 00167 } 00168 00169 00170 /*----------------------------------------------------------------------* 00171 * Accessors * 00172 *----------------------------------------------------------------------*/ 00173 /*! 00174 * kernelGetElement() 00175 * 00176 * Input: kel 00177 * row 00178 * col 00179 * &val 00180 * Return: 0 if OK; 1 on error 00181 */ 00182 l_int32 00183 kernelGetElement(L_KERNEL *kel, 00184 l_int32 row, 00185 l_int32 col, 00186 l_float32 *pval) 00187 { 00188 PROCNAME("kernelGetElement"); 00189 00190 if (!pval) 00191 return ERROR_INT("&val not defined", procName, 1); 00192 *pval = 0; 00193 if (!kel) 00194 return ERROR_INT("kernel not defined", procName, 1); 00195 if (row < 0 || row >= kel->sy) 00196 return ERROR_INT("kernel row out of bounds", procName, 1); 00197 if (col < 0 || col >= kel->sx) 00198 return ERROR_INT("kernel col out of bounds", procName, 1); 00199 00200 *pval = kel->data[row][col]; 00201 return 0; 00202 } 00203 00204 00205 /*! 00206 * kernelSetElement() 00207 * 00208 * Input: kernel 00209 * row 00210 * col 00211 * val 00212 * Return: 0 if OK; 1 on error 00213 */ 00214 l_int32 00215 kernelSetElement(L_KERNEL *kel, 00216 l_int32 row, 00217 l_int32 col, 00218 l_float32 val) 00219 { 00220 PROCNAME("kernelSetElement"); 00221 00222 if (!kel) 00223 return ERROR_INT("kel not defined", procName, 1); 00224 if (row < 0 || row >= kel->sy) 00225 return ERROR_INT("kernel row out of bounds", procName, 1); 00226 if (col < 0 || col >= kel->sx) 00227 return ERROR_INT("kernel col out of bounds", procName, 1); 00228 00229 kel->data[row][col] = val; 00230 return 0; 00231 } 00232 00233 00234 /*! 00235 * kernelGetParameters() 00236 * 00237 * Input: kernel 00238 * &sy, &sx, &cy, &cx (<optional return>; each can be null) 00239 * Return: 0 if OK, 1 on error 00240 */ 00241 l_int32 00242 kernelGetParameters(L_KERNEL *kel, 00243 l_int32 *psy, 00244 l_int32 *psx, 00245 l_int32 *pcy, 00246 l_int32 *pcx) 00247 { 00248 PROCNAME("kernelGetParameters"); 00249 00250 if (psy) *psy = 0; 00251 if (psx) *psx = 0; 00252 if (pcy) *pcy = 0; 00253 if (pcx) *pcx = 0; 00254 if (!kel) 00255 return ERROR_INT("kernel not defined", procName, 1); 00256 if (psy) *psy = kel->sy; 00257 if (psx) *psx = kel->sx; 00258 if (pcy) *pcy = kel->cy; 00259 if (pcx) *pcx = kel->cx; 00260 return 0; 00261 } 00262 00263 00264 /*! 00265 * kernelSetOrigin() 00266 * 00267 * Input: kernel 00268 * cy, cx 00269 * Return: 0 if OK; 1 on error 00270 */ 00271 l_int32 00272 kernelSetOrigin(L_KERNEL *kel, 00273 l_int32 cy, 00274 l_int32 cx) 00275 { 00276 PROCNAME("kernelSetOrigin"); 00277 00278 if (!kel) 00279 return ERROR_INT("kel not defined", procName, 1); 00280 kel->cy = cy; 00281 kel->cx = cx; 00282 return 0; 00283 } 00284 00285 00286 /*! 00287 * kernelGetSum() 00288 * 00289 * Input: kernel 00290 * &sum (<return> sum of all kernel values) 00291 * Return: 0 if OK, 1 on error 00292 */ 00293 l_int32 00294 kernelGetSum(L_KERNEL *kel, 00295 l_float32 *psum) 00296 { 00297 l_int32 sx, sy, i, j; 00298 00299 PROCNAME("kernelGetSum"); 00300 00301 if (!psum) 00302 return ERROR_INT("&sum not defined", procName, 1); 00303 *psum = 0.0; 00304 if (!kel) 00305 return ERROR_INT("kernel not defined", procName, 1); 00306 00307 kernelGetParameters(kel, &sy, &sx, NULL, NULL); 00308 for (i = 0; i < sy; i++) { 00309 for (j = 0; j < sx; j++) { 00310 *psum += kel->data[i][j]; 00311 } 00312 } 00313 return 0; 00314 } 00315 00316 00317 /*! 00318 * kernelGetMinMax() 00319 * 00320 * Input: kernel 00321 * &min (<optional return> minimum value) 00322 * &max (<optional return> maximum value) 00323 * Return: 0 if OK, 1 on error 00324 */ 00325 l_int32 00326 kernelGetMinMax(L_KERNEL *kel, 00327 l_float32 *pmin, 00328 l_float32 *pmax) 00329 { 00330 l_int32 sx, sy, i, j; 00331 l_float32 val, minval, maxval; 00332 00333 PROCNAME("kernelGetMinmax"); 00334 00335 if (!pmin && !pmax) 00336 return ERROR_INT("neither &min nor &max defined", procName, 1); 00337 if (pmin) *pmin = 0.0; 00338 if (pmax) *pmax = 0.0; 00339 if (!kel) 00340 return ERROR_INT("kernel not defined", procName, 1); 00341 00342 kernelGetParameters(kel, &sy, &sx, NULL, NULL); 00343 minval = 10000000.0; 00344 maxval = -10000000.0; 00345 for (i = 0; i < sy; i++) { 00346 for (j = 0; j < sx; j++) { 00347 val = kel->data[i][j]; 00348 if (val < minval) 00349 minval = val; 00350 if (val > maxval) 00351 maxval = val; 00352 } 00353 } 00354 if (pmin) 00355 *pmin = minval; 00356 if (pmax) 00357 *pmax = maxval; 00358 00359 return 0; 00360 } 00361 00362 00363 /*----------------------------------------------------------------------* 00364 * Normalize/Invert * 00365 *----------------------------------------------------------------------*/ 00366 /*! 00367 * kernelNormalize() 00368 * 00369 * Input: kels (source kel, to be normalized) 00370 * normsum (desired sum of elements in keld) 00371 * Return: keld (normalized version of kels), or null on error 00372 * or if sum of elements is very close to 0) 00373 * 00374 * Notes: 00375 * (1) If the sum of kernel elements is close to 0, do not 00376 * try to calculate the normalized kernel. Instead, 00377 * return a copy of the input kernel, with an error message. 00378 */ 00379 L_KERNEL * 00380 kernelNormalize(L_KERNEL *kels, 00381 l_float32 normsum) 00382 { 00383 l_int32 i, j, sx, sy, cx, cy; 00384 l_float32 sum, factor; 00385 L_KERNEL *keld; 00386 00387 PROCNAME("kernelNormalize"); 00388 00389 if (!kels) 00390 return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); 00391 00392 kernelGetSum(kels, &sum); 00393 if (L_ABS(sum) < 0.01) { 00394 L_ERROR("null sum; not normalizing; returning a copy", procName); 00395 return kernelCopy(kels); 00396 } 00397 00398 kernelGetParameters(kels, &sy, &sx, &cy, &cx); 00399 if ((keld = kernelCreate(sy, sx)) == NULL) 00400 return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); 00401 keld->cy = cy; 00402 keld->cx = cx; 00403 00404 factor = normsum / sum; 00405 for (i = 0; i < sy; i++) 00406 for (j = 0; j < sx; j++) 00407 keld->data[i][j] = factor * kels->data[i][j]; 00408 00409 return keld; 00410 } 00411 00412 00413 /*! 00414 * kernelInvert() 00415 * 00416 * Input: kels (source kel, to be inverted) 00417 * Return: keld (spatially inverted, about the origin), or null on error 00418 * 00419 * Notes: 00420 * (1) For convolution, the kernel is spatially inverted before 00421 * a "correlation" operation is done between the kernel and the image. 00422 */ 00423 L_KERNEL * 00424 kernelInvert(L_KERNEL *kels) 00425 { 00426 l_int32 i, j, sx, sy, cx, cy; 00427 L_KERNEL *keld; 00428 00429 PROCNAME("kernelInvert"); 00430 00431 if (!kels) 00432 return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); 00433 00434 kernelGetParameters(kels, &sy, &sx, &cy, &cx); 00435 if ((keld = kernelCreate(sy, sx)) == NULL) 00436 return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); 00437 keld->cy = sy - 1 - cy; 00438 keld->cx = sx - 1 - cx; 00439 00440 for (i = 0; i < sy; i++) 00441 for (j = 0; j < sx; j++) 00442 keld->data[i][j] = kels->data[sy - 1 - i][sx - 1 - j]; 00443 00444 return keld; 00445 } 00446 00447 00448 /*----------------------------------------------------------------------* 00449 * Helper function * 00450 *----------------------------------------------------------------------*/ 00451 /*! 00452 * create2dFloatArray() 00453 * 00454 * Input: sy (rows == height) 00455 * sx (columns == width) 00456 * Return: doubly indexed array (i.e., an array of sy row pointers, 00457 * each of which points to an array of sx floats) 00458 * 00459 * Notes: 00460 * (1) The array[sy][sx] is indexed in standard "matrix notation", 00461 * with the row index first. 00462 */ 00463 l_float32 ** 00464 create2dFloatArray(l_int32 sy, 00465 l_int32 sx) 00466 { 00467 l_int32 i; 00468 l_float32 **array; 00469 00470 PROCNAME("create2dFloatArray"); 00471 00472 if ((array = (l_float32 **)CALLOC(sy, sizeof(l_float32 *))) == NULL) 00473 return (l_float32 **)ERROR_PTR("ptr array not made", procName, NULL); 00474 00475 for (i = 0; i < sy; i++) { 00476 if ((array[i] = (l_float32 *)CALLOC(sx, sizeof(l_float32))) == NULL) 00477 return (l_float32 **)ERROR_PTR("array not made", procName, NULL); 00478 } 00479 00480 return array; 00481 } 00482 00483 00484 /*----------------------------------------------------------------------* 00485 * Kernel serialized I/O * 00486 *----------------------------------------------------------------------*/ 00487 /*! 00488 * kernelRead() 00489 * 00490 * Input: filename 00491 * Return: kernel, or null on error 00492 */ 00493 L_KERNEL * 00494 kernelRead(const char *fname) 00495 { 00496 FILE *fp; 00497 L_KERNEL *kel; 00498 00499 PROCNAME("kernelRead"); 00500 00501 if (!fname) 00502 return (L_KERNEL *)ERROR_PTR("fname not defined", procName, NULL); 00503 00504 if ((fp = fopenReadStream(fname)) == NULL) 00505 return (L_KERNEL *)ERROR_PTR("stream not opened", procName, NULL); 00506 if ((kel = kernelReadStream(fp)) == NULL) 00507 return (L_KERNEL *)ERROR_PTR("kel not returned", procName, NULL); 00508 fclose(fp); 00509 00510 return kel; 00511 } 00512 00513 00514 /*! 00515 * kernelReadStream() 00516 * 00517 * Input: stream 00518 * Return: kernel, or null on error 00519 */ 00520 L_KERNEL * 00521 kernelReadStream(FILE *fp) 00522 { 00523 l_int32 sy, sx, cy, cx, i, j, ret, version, ignore; 00524 L_KERNEL *kel; 00525 00526 PROCNAME("kernelReadStream"); 00527 00528 if (!fp) 00529 return (L_KERNEL *)ERROR_PTR("stream not defined", procName, NULL); 00530 00531 ret = fscanf(fp, " Kernel Version %d\n", &version); 00532 if (ret != 1) 00533 return (L_KERNEL *)ERROR_PTR("not a kernel file", procName, NULL); 00534 if (version != KERNEL_VERSION_NUMBER) 00535 return (L_KERNEL *)ERROR_PTR("invalid kernel version", procName, NULL); 00536 00537 if (fscanf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", 00538 &sy, &sx, &cy, &cx) != 4) 00539 return (L_KERNEL *)ERROR_PTR("dimensions not read", procName, NULL); 00540 00541 if ((kel = kernelCreate(sy, sx)) == NULL) 00542 return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); 00543 kernelSetOrigin(kel, cy, cx); 00544 00545 for (i = 0; i < sy; i++) { 00546 for (j = 0; j < sx; j++) 00547 ignore = fscanf(fp, "%15f", &kel->data[i][j]); 00548 ignore = fscanf(fp, "\n"); 00549 } 00550 ignore = fscanf(fp, "\n"); 00551 00552 return kel; 00553 } 00554 00555 00556 /*! 00557 * kernelWrite() 00558 * 00559 * Input: fname (output file) 00560 * kernel 00561 * Return: 0 if OK, 1 on error 00562 */ 00563 l_int32 00564 kernelWrite(const char *fname, 00565 L_KERNEL *kel) 00566 { 00567 FILE *fp; 00568 00569 PROCNAME("kernelWrite"); 00570 00571 if (!fname) 00572 return ERROR_INT("fname not defined", procName, 1); 00573 if (!kel) 00574 return ERROR_INT("kel not defined", procName, 1); 00575 00576 if ((fp = fopenWriteStream(fname, "wb")) == NULL) 00577 return ERROR_INT("stream not opened", procName, 1); 00578 kernelWriteStream(fp, kel); 00579 fclose(fp); 00580 00581 return 0; 00582 } 00583 00584 00585 /*! 00586 * kernelWriteStream() 00587 * 00588 * Input: stream 00589 * kel 00590 * Return: 0 if OK, 1 on error 00591 */ 00592 l_int32 00593 kernelWriteStream(FILE *fp, 00594 L_KERNEL *kel) 00595 { 00596 l_int32 sx, sy, cx, cy, i, j; 00597 00598 PROCNAME("kernelWriteStream"); 00599 00600 if (!fp) 00601 return ERROR_INT("stream not defined", procName, 1); 00602 if (!kel) 00603 return ERROR_INT("kel not defined", procName, 1); 00604 kernelGetParameters(kel, &sy, &sx, &cy, &cx); 00605 00606 fprintf(fp, " Kernel Version %d\n", KERNEL_VERSION_NUMBER); 00607 fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); 00608 for (i = 0; i < sy; i++) { 00609 for (j = 0; j < sx; j++) 00610 fprintf(fp, "%15.4f", kel->data[i][j]); 00611 fprintf(fp, "\n"); 00612 } 00613 fprintf(fp, "\n"); 00614 00615 return 0; 00616 } 00617 00618 00619 /*----------------------------------------------------------------------* 00620 * Making a kernel from a compiled string * 00621 *----------------------------------------------------------------------*/ 00622 /*! 00623 * kernelCreateFromString() 00624 * 00625 * Input: height, width 00626 * cy, cx (origin) 00627 * kdata 00628 * Return: kernel of the given size, or null on error 00629 * 00630 * Notes: 00631 * (1) The data is an array of chars, in row-major order, giving 00632 * space separated integers in the range [-255 ... 255]. 00633 * (2) The only other formatting limitation is that you must 00634 * leave space between the last number in each row and 00635 * the double-quote. If possible, it's also nice to have each 00636 * line in the string represent a line in the kernel; e.g., 00637 * static const char *kdata = 00638 * " 20 50 20 " 00639 * " 70 140 70 " 00640 * " 20 50 20 "; 00641 */ 00642 L_KERNEL * 00643 kernelCreateFromString(l_int32 h, 00644 l_int32 w, 00645 l_int32 cy, 00646 l_int32 cx, 00647 const char *kdata) 00648 { 00649 l_int32 n, i, j, index; 00650 l_float32 val; 00651 L_KERNEL *kel; 00652 NUMA *na; 00653 00654 PROCNAME("kernelCreateFromString"); 00655 00656 if (h < 1) 00657 return (L_KERNEL *)ERROR_PTR("height must be > 0", procName, NULL); 00658 if (w < 1) 00659 return (L_KERNEL *)ERROR_PTR("width must be > 0", procName, NULL); 00660 if (cy < 0 || cy >= h) 00661 return (L_KERNEL *)ERROR_PTR("cy invalid", procName, NULL); 00662 if (cx < 0 || cx >= w) 00663 return (L_KERNEL *)ERROR_PTR("cx invalid", procName, NULL); 00664 00665 kel = kernelCreate(h, w); 00666 kernelSetOrigin(kel, cy, cx); 00667 na = parseStringForNumbers(kdata, " \t\n"); 00668 n = numaGetCount(na); 00669 if (n != w * h) { 00670 numaDestroy(&na); 00671 fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n); 00672 return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); 00673 } 00674 00675 index = 0; 00676 for (i = 0; i < h; i++) { 00677 for (j = 0; j < w; j++) { 00678 numaGetFValue(na, index, &val); 00679 kernelSetElement(kel, i, j, val); 00680 index++; 00681 } 00682 } 00683 00684 numaDestroy(&na); 00685 return kel; 00686 } 00687 00688 00689 /*----------------------------------------------------------------------* 00690 * Making a kernel from a simple file format * 00691 *----------------------------------------------------------------------*/ 00692 /*! 00693 * kernelCreateFromFile() 00694 * 00695 * Input: filename 00696 * Return: kernel, or null on error 00697 * 00698 * Notes: 00699 * (1) The file contains, in the following order: 00700 * - Any number of comment lines starting with '#' are ignored 00701 * - The height and width of the kernel 00702 * - The y and x values of the kernel origin 00703 * - The kernel data, formatted as lines of numbers (integers 00704 * or floats) for the kernel values in row-major order, 00705 * and with no other punctuation. 00706 * (Note: this differs from kernelCreateFromString(), 00707 * where each line must begin and end with a double-quote 00708 * to tell the compiler it's part of a string.) 00709 * - The kernel specification ends when a blank line, 00710 * a comment line, or the end of file is reached. 00711 * (2) All lines must be left-justified. 00712 * (3) See kernelCreateFromString() for a description of the string 00713 * format for the kernel data. As an example, here are the lines 00714 * of a valid kernel description file In the file, all lines 00715 * are left-justified: 00716 * # small 3x3 kernel 00717 * 3 3 00718 * 1 1 00719 * 25.5 51 24.3 00720 * 70.2 146.3 73.4 00721 * 20 50.9 18.4 00722 */ 00723 L_KERNEL * 00724 kernelCreateFromFile(const char *filename) 00725 { 00726 char *filestr, *line; 00727 l_int32 nlines, i, j, first, index, w, h, cx, cy, n; 00728 l_float32 val; 00729 size_t size; 00730 NUMA *na, *nat; 00731 SARRAY *sa; 00732 L_KERNEL *kel; 00733 00734 PROCNAME("kernelCreateFromFile"); 00735 00736 if (!filename) 00737 return (L_KERNEL *)ERROR_PTR("filename not defined", procName, NULL); 00738 00739 filestr = (char *)l_binaryRead(filename, &size); 00740 sa = sarrayCreateLinesFromString(filestr, 1); 00741 FREE(filestr); 00742 nlines = sarrayGetCount(sa); 00743 00744 /* Find the first data line. */ 00745 for (i = 0; i < nlines; i++) { 00746 line = sarrayGetString(sa, i, L_NOCOPY); 00747 if (line[0] != '#') { 00748 first = i; 00749 break; 00750 } 00751 } 00752 00753 /* Find the kernel dimensions and origin location. */ 00754 line = sarrayGetString(sa, first, L_NOCOPY); 00755 if (sscanf(line, "%d %d", &h, &w) != 2) 00756 return (L_KERNEL *)ERROR_PTR("error reading h,w", procName, NULL); 00757 line = sarrayGetString(sa, first + 1, L_NOCOPY); 00758 if (sscanf(line, "%d %d", &cy, &cx) != 2) 00759 return (L_KERNEL *)ERROR_PTR("error reading cy,cx", procName, NULL); 00760 00761 /* Extract the data. This ends when we reach eof, or when we 00762 * encounter a line of data that is either a null string or 00763 * contains just a newline. */ 00764 na = numaCreate(0); 00765 for (i = first + 2; i < nlines; i++) { 00766 line = sarrayGetString(sa, i, L_NOCOPY); 00767 if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') 00768 break; 00769 nat = parseStringForNumbers(line, " \t\n"); 00770 numaJoin(na, nat, 0, 0); 00771 numaDestroy(&nat); 00772 } 00773 sarrayDestroy(&sa); 00774 00775 n = numaGetCount(na); 00776 if (n != w * h) { 00777 numaDestroy(&na); 00778 fprintf(stderr, "w = %d, h = %d, num ints = %d\n", w, h, n); 00779 return (L_KERNEL *)ERROR_PTR("invalid integer data", procName, NULL); 00780 } 00781 00782 kel = kernelCreate(h, w); 00783 kernelSetOrigin(kel, cy, cx); 00784 index = 0; 00785 for (i = 0; i < h; i++) { 00786 for (j = 0; j < w; j++) { 00787 numaGetFValue(na, index, &val); 00788 kernelSetElement(kel, i, j, val); 00789 index++; 00790 } 00791 } 00792 00793 numaDestroy(&na); 00794 return kel; 00795 } 00796 00797 00798 /*----------------------------------------------------------------------* 00799 * Making a kernel from a Pix * 00800 *----------------------------------------------------------------------*/ 00801 /*! 00802 * kernelCreateFromPix() 00803 * 00804 * Input: pix 00805 * cy, cx (origin of kernel) 00806 * Return: kernel, or null on error 00807 * 00808 * Notes: 00809 * (1) The origin must be positive and within the dimensions of the pix. 00810 */ 00811 L_KERNEL * 00812 kernelCreateFromPix(PIX *pix, 00813 l_int32 cy, 00814 l_int32 cx) 00815 { 00816 l_int32 i, j, w, h, d; 00817 l_uint32 val; 00818 L_KERNEL *kel; 00819 00820 PROCNAME("kernelCreateFromPix"); 00821 00822 if (!pix) 00823 return (L_KERNEL *)ERROR_PTR("pix not defined", procName, NULL); 00824 pixGetDimensions(pix, &w, &h, &d); 00825 if (d != 8) 00826 return (L_KERNEL *)ERROR_PTR("pix not 8 bpp", procName, NULL); 00827 if (cy < 0 || cx < 0 || cy >= h || cx >= w) 00828 return (L_KERNEL *)ERROR_PTR("(cy, cx) invalid", procName, NULL); 00829 00830 kel = kernelCreate(h, w); 00831 kernelSetOrigin(kel, cy, cx); 00832 for (i = 0; i < h; i++) { 00833 for (j = 0; j < w; j++) { 00834 pixGetPixel(pix, j, i, &val); 00835 kernelSetElement(kel, i, j, (l_float32)val); 00836 } 00837 } 00838 00839 return kel; 00840 } 00841 00842 00843 /*----------------------------------------------------------------------* 00844 * Display a kernel in a pix * 00845 *----------------------------------------------------------------------*/ 00846 /*! 00847 * kernelDisplayInPix() 00848 * 00849 * Input: kernel 00850 * size (of grid interiors; odd; minimum size of 17 is enforced) 00851 * gthick (grid thickness; minimum size of 2 is enforced) 00852 * Return: pix (display of kernel), or null on error 00853 * 00854 * Notes: 00855 * (1) This gives a visual representation of a kernel. 00856 * (2) The origin is outlined in red. 00857 */ 00858 PIX * 00859 kernelDisplayInPix(L_KERNEL *kel, 00860 l_int32 size, 00861 l_int32 gthick) 00862 { 00863 l_int32 i, j, w, h, sx, sy, cx, cy, width, x0, y0; 00864 l_int32 normval; 00865 l_float32 minval, maxval, max, val, norm; 00866 PIX *pixd, *pixt0, *pixt1; 00867 00868 PROCNAME("kernelDisplayInPix"); 00869 00870 if (!kel) 00871 return (PIX *)ERROR_PTR("kernel not defined", procName, NULL); 00872 if (size < 17) { 00873 L_WARNING("size < 17; setting to 17", procName); 00874 size = 17; 00875 } 00876 if (size % 2 == 0) 00877 size++; 00878 if (gthick < 2) { 00879 L_WARNING("grid thickness < 2; setting to 2", procName); 00880 gthick = 2; 00881 } 00882 00883 /* Normalize the max value to be 255 for display */ 00884 kernelGetParameters(kel, &sy, &sx, &cy, &cx); 00885 kernelGetMinMax(kel, &minval, &maxval); 00886 max = L_MAX(maxval, -minval); 00887 norm = 255. / (l_float32)max; 00888 w = size * sx + gthick * (sx + 1); 00889 h = size * sy + gthick * (sy + 1); 00890 pixd = pixCreate(w, h, 8); 00891 00892 /* Generate grid lines */ 00893 for (i = 0; i <= sy; i++) 00894 pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), 00895 w - 1, gthick / 2 + i * (size + gthick), 00896 gthick, L_SET_PIXELS); 00897 for (j = 0; j <= sx; j++) 00898 pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, 00899 gthick / 2 + j * (size + gthick), h - 1, 00900 gthick, L_SET_PIXELS); 00901 00902 /* Generate mask for each element */ 00903 pixt0 = pixCreate(size, size, 1); 00904 pixSetAll(pixt0); 00905 00906 /* Generate crossed lines for origin pattern */ 00907 pixt1 = pixCreate(size, size, 1); 00908 width = size / 8; 00909 pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size), 00910 size / 2, (l_int32)(0.88 * size), 00911 width, L_SET_PIXELS); 00912 pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2, 00913 (l_int32)(0.85 * size), size / 2, 00914 width, L_FLIP_PIXELS); 00915 pixRasterop(pixt1, size / 2 - width, size / 2 - width, 00916 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); 00917 00918 /* Paste the patterns in */ 00919 y0 = gthick; 00920 for (i = 0; i < sy; i++) { 00921 x0 = gthick; 00922 for (j = 0; j < sx; j++) { 00923 kernelGetElement(kel, i, j, &val); 00924 normval = (l_int32)(norm * L_ABS(val)); 00925 pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0); 00926 if (i == cy && j == cx) 00927 pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval); 00928 x0 += size + gthick; 00929 } 00930 y0 += size + gthick; 00931 } 00932 00933 pixDestroy(&pixt0); 00934 pixDestroy(&pixt1); 00935 return pixd; 00936 } 00937 00938 00939 /*------------------------------------------------------------------------* 00940 * Parse string to extract numbers * 00941 *------------------------------------------------------------------------*/ 00942 /*! 00943 * parseStringForNumbers() 00944 * 00945 * Input: string (containing numbers; not changed) 00946 * seps (string of characters that can be used between ints) 00947 * Return: numa (of numbers found), or null on error 00948 * 00949 * Note: 00950 * (1) The numbers can be ints or floats. 00951 */ 00952 NUMA * 00953 parseStringForNumbers(const char *str, 00954 const char *seps) 00955 { 00956 char *newstr, *head, *tail; 00957 l_float32 val; 00958 NUMA *na; 00959 00960 PROCNAME("parseStringForNumbers"); 00961 00962 if (!str) 00963 return (NUMA *)ERROR_PTR("str not defined", procName, NULL); 00964 00965 newstr = stringNew(str); /* to enforce const-ness of str */ 00966 na = numaCreate(0); 00967 head = strtokSafe(newstr, seps, &tail); 00968 val = atof(head); 00969 numaAddNumber(na, val); 00970 FREE(head); 00971 while ((head = strtokSafe(NULL, seps, &tail)) != NULL) { 00972 val = atof(head); 00973 numaAddNumber(na, val); 00974 FREE(head); 00975 } 00976 00977 FREE(newstr); 00978 return na; 00979 } 00980 00981 00982 /*------------------------------------------------------------------------* 00983 * Simple parametric kernels * 00984 *------------------------------------------------------------------------*/ 00985 /*! 00986 * makeFlatKernel() 00987 * 00988 * Input: height, width 00989 * cy, cx (origin of kernel) 00990 * Return: kernel, or null on error 00991 * 00992 * Notes: 00993 * (1) This is the same low-pass filtering kernel that is used 00994 * in the block convolution functions. 00995 * (2) The kernel origin (@cy, @cx) is typically placed as near 00996 * the center of the kernel as possible. If height and 00997 * width are odd, then using cy = (height - 1) / 2 and 00998 * cx = (width - 1) / 2 places the origin at the exact center. 00999 * (3) This returns a normalized kernel. 01000 */ 01001 L_KERNEL * 01002 makeFlatKernel(l_int32 height, 01003 l_int32 width, 01004 l_int32 cy, 01005 l_int32 cx) 01006 { 01007 l_int32 i, j; 01008 l_float32 normval; 01009 L_KERNEL *kel; 01010 01011 PROCNAME("makeFlatKernel"); 01012 01013 if ((kel = kernelCreate(height, width)) == NULL) 01014 return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); 01015 kernelSetOrigin(kel, cy, cx); 01016 normval = 1.0 / (l_float32)(height * width); 01017 for (i = 0; i < height; i++) { 01018 for (j = 0; j < width; j++) { 01019 kernelSetElement(kel, i, j, normval); 01020 } 01021 } 01022 01023 return kel; 01024 } 01025 01026 01027 /*! 01028 * makeGaussianKernel() 01029 * 01030 * Input: halfheight, halfwidth (sx = 2 * halfwidth + 1, etc) 01031 * stdev (standard deviation) 01032 * max (value at (cx,cy)) 01033 * Return: kernel, or null on error 01034 * 01035 * Notes: 01036 * (1) The kernel size (sx, sy) = (2 * halfwidth + 1, 2 * halfheight + 1). 01037 * (2) The kernel center (cx, cy) = (halfwidth, halfheight). 01038 * (3) The halfwidth and halfheight are typically equal, and 01039 * are typically several times larger than the standard deviation. 01040 * (4) If pixConvolve() is invoked with normalization (the sum of 01041 * kernel elements = 1.0), use 1.0 for max (or any number that's 01042 * not too small or too large). 01043 */ 01044 L_KERNEL * 01045 makeGaussianKernel(l_int32 halfheight, 01046 l_int32 halfwidth, 01047 l_float32 stdev, 01048 l_float32 max) 01049 { 01050 l_int32 sx, sy, i, j; 01051 l_float32 val; 01052 L_KERNEL *kel; 01053 01054 PROCNAME("makeGaussianKernel"); 01055 01056 sx = 2 * halfwidth + 1; 01057 sy = 2 * halfheight + 1; 01058 if ((kel = kernelCreate(sy, sx)) == NULL) 01059 return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); 01060 kernelSetOrigin(kel, halfheight, halfwidth); 01061 for (i = 0; i < sy; i++) { 01062 for (j = 0; j < sx; j++) { 01063 val = expf(-(l_float32)((i - halfheight) * (i - halfheight) + 01064 (j - halfwidth) * (j - halfwidth)) / 01065 (2. * stdev * stdev)); 01066 kernelSetElement(kel, i, j, max * val); 01067 } 01068 } 01069 01070 return kel; 01071 } 01072 01073 01074 /*! 01075 * makeGaussianKernelSep() 01076 * 01077 * Input: halfheight, halfwidth (sx = 2 * halfwidth + 1, etc) 01078 * stdev (standard deviation) 01079 * max (value at (cx,cy)) 01080 * &kelx (<return> x part of kernel) 01081 * &kely (<return> y part of kernel) 01082 * Return: 0 if OK, 1 on error 01083 * 01084 * Notes: 01085 * (1) See makeGaussianKernel() for description of input parameters. 01086 * (2) These kernels are constructed so that the result of both 01087 * normalized and un-normalized convolution will be the same 01088 * as when convolving with pixConvolve() using the full kernel. 01089 * (3) The trick for the un-normalized convolution is to have the 01090 * product of the two kernel elemets at (cx,cy) be equal to max, 01091 * not max**2. That's why the max for kely is 1.0. If instead 01092 * we use sqrt(max) for both, the results are slightly less 01093 * accurate, when compared to using the full kernel in 01094 * makeGaussianKernel(). 01095 */ 01096 l_int32 01097 makeGaussianKernelSep(l_int32 halfheight, 01098 l_int32 halfwidth, 01099 l_float32 stdev, 01100 l_float32 max, 01101 L_KERNEL **pkelx, 01102 L_KERNEL **pkely) 01103 { 01104 PROCNAME("makeGaussianKernelSep"); 01105 01106 if (!pkelx || !pkely) 01107 return ERROR_INT("&kelx and &kely not defined", procName, 1); 01108 01109 *pkelx = makeGaussianKernel(0, halfwidth, stdev, max); 01110 *pkely = makeGaussianKernel(halfheight, 0, stdev, 1.0); 01111 return 0; 01112 } 01113 01114 01115 /*! 01116 * makeDoGKernel() 01117 * 01118 * Input: halfheight, halfwidth (sx = 2 * halfwidth + 1, etc) 01119 * stdev (standard deviation) 01120 * ratio (of stdev for wide filter to stdev for narrow one) 01121 * Return: kernel, or null on error 01122 * 01123 * Notes: 01124 * (1) The DoG (difference of gaussians) is a wavelet mother 01125 * function with null total sum. By subtracting two blurred 01126 * versions of the image, it acts as a bandpass filter for 01127 * frequencies passed by the narrow gaussian but stopped 01128 * by the wide one.See: 01129 * http://en.wikipedia.org/wiki/Difference_of_Gaussians 01130 * (2) The kernel size (sx, sy) = (2 * halfwidth + 1, 2 * halfheight + 1). 01131 * (3) The kernel center (cx, cy) = (halfwidth, halfheight). 01132 * (4) The halfwidth and halfheight are typically equal, and 01133 * are typically several times larger than the standard deviation. 01134 * (5) The ratio is the ratio of standard deviations of the wide 01135 * to narrow gaussian. It must be >= 1.0; 1.0 is a no-op. 01136 * (6) Because the kernel is a null sum, it must be invoked without 01137 * normalization in pixConvolve(). 01138 */ 01139 L_KERNEL * 01140 makeDoGKernel(l_int32 halfheight, 01141 l_int32 halfwidth, 01142 l_float32 stdev, 01143 l_float32 ratio) 01144 { 01145 l_int32 sx, sy, i, j; 01146 l_float32 pi, squaredist, highnorm, lownorm, val; 01147 L_KERNEL *kel; 01148 01149 PROCNAME("makeDoGKernel"); 01150 01151 sx = 2 * halfwidth + 1; 01152 sy = 2 * halfheight + 1; 01153 if ((kel = kernelCreate(sy, sx)) == NULL) 01154 return (L_KERNEL *)ERROR_PTR("kel not made", procName, NULL); 01155 kernelSetOrigin(kel, halfheight, halfwidth); 01156 01157 pi = 3.1415926535; 01158 for (i = 0; i < sy; i++) { 01159 for (j = 0; j < sx; j++) { 01160 squaredist = (l_float32)((i - halfheight) * (i - halfheight) + 01161 (j - halfwidth) * (j - halfwidth)); 01162 highnorm = 1. / (2 * stdev * stdev); 01163 lownorm = highnorm / (ratio * ratio); 01164 val = (highnorm / pi) * expf(-(highnorm * squaredist)) - 01165 (lownorm / pi) * expf(-(lownorm * squaredist)); 01166 kernelSetElement(kel, i, j, val); 01167 } 01168 } 01169 01170 return kel; 01171 } 01172 01173