Leptonica 1.68
C Image Processing Library

kernel.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines