Coverage Report

Created: 2024-07-27 06:27

/src/leptonica/src/kernel.c
Line
Count
Source (jump to first uncovered line)
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
0
{
115
0
l_uint64   size64;
116
0
L_KERNEL  *kel;
117
118
0
    if (width <= 0)
119
0
        return (L_KERNEL *)ERROR_PTR("width must be > 0", __func__, NULL);
120
0
    if (height <= 0)
121
0
        return (L_KERNEL *)ERROR_PTR("height must be > 0", __func__, NULL);
122
123
        /* Avoid overflow in malloc arg */
124
0
    size64 = (l_uint64)width * (l_uint64)height;
125
0
    if (size64 >= (1LL << 29)) {
126
0
        L_ERROR("requested width = %d, height = %d\n", __func__, width, height);
127
0
        return (L_KERNEL *)ERROR_PTR("size >= 2^29", __func__, NULL);
128
0
    }
129
130
0
    kel = (L_KERNEL *)LEPT_CALLOC(1, sizeof(L_KERNEL));
131
0
    kel->sy = height;
132
0
    kel->sx = width;
133
0
    if ((kel->data = create2dFloatArray(height, width)) == NULL) {
134
0
        LEPT_FREE(kel);
135
0
        return (L_KERNEL *)ERROR_PTR("data not allocated", __func__, NULL);
136
0
    }
137
0
    return kel;
138
0
}
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
0
{
150
0
l_int32    i;
151
0
L_KERNEL  *kel;
152
153
0
    if (pkel == NULL)  {
154
0
        L_WARNING("ptr address is NULL!\n", __func__);
155
0
        return;
156
0
    }
157
0
    if ((kel = *pkel) == NULL)
158
0
        return;
159
160
0
    for (i = 0; i < kel->sy; i++)
161
0
        LEPT_FREE(kel->data[i]);
162
0
    LEPT_FREE(kel->data);
163
0
    LEPT_FREE(kel);
164
0
    *pkel = NULL;
165
0
}
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
0
{
177
0
l_int32    i, j, sx, sy, cx, cy;
178
0
L_KERNEL  *keld;
179
180
0
    if (!kels)
181
0
        return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL);
182
183
0
    kernelGetParameters(kels, &sy, &sx, &cy, &cx);
184
0
    if ((keld = kernelCreate(sy, sx)) == NULL)
185
0
        return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL);
186
0
    keld->cy = cy;
187
0
    keld->cx = cx;
188
0
    for (i = 0; i < sy; i++)
189
0
        for (j = 0; j < sx; j++)
190
0
            keld->data[i][j] = kels->data[i][j];
191
192
0
    return keld;
193
0
}
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
0
{
214
0
    if (!pval)
215
0
        return ERROR_INT("&val not defined", __func__, 1);
216
0
    *pval = 0;
217
0
    if (!kel)
218
0
        return ERROR_INT("kernel not defined", __func__, 1);
219
0
    if (row < 0 || row >= kel->sy)
220
0
        return ERROR_INT("kernel row out of bounds", __func__, 1);
221
0
    if (col < 0 || col >= kel->sx)
222
0
        return ERROR_INT("kernel col out of bounds", __func__, 1);
223
224
0
    *pval = kel->data[row][col];
225
0
    return 0;
226
0
}
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
0
{
244
0
    if (!kel)
245
0
        return ERROR_INT("kel not defined", __func__, 1);
246
0
    if (row < 0 || row >= kel->sy)
247
0
        return ERROR_INT("kernel row out of bounds", __func__, 1);
248
0
    if (col < 0 || col >= kel->sx)
249
0
        return ERROR_INT("kernel col out of bounds", __func__, 1);
250
251
0
    kel->data[row][col] = val;
252
0
    return 0;
253
0
}
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
0
{
270
0
    if (psy) *psy = 0;
271
0
    if (psx) *psx = 0;
272
0
    if (pcy) *pcy = 0;
273
0
    if (pcx) *pcx = 0;
274
0
    if (!kel)
275
0
        return ERROR_INT("kernel not defined", __func__, 1);
276
0
    if (psy) *psy = kel->sy;
277
0
    if (psx) *psx = kel->sx;
278
0
    if (pcy) *pcy = kel->cy;
279
0
    if (pcx) *pcx = kel->cx;
280
0
    return 0;
281
0
}
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
0
{
296
0
    if (!kel)
297
0
        return ERROR_INT("kel not defined", __func__, 1);
298
0
    kel->cy = cy;
299
0
    kel->cx = cx;
300
0
    return 0;
301
0
}
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
0
{
315
0
l_int32    sx, sy, i, j;
316
317
0
    if (!psum)
318
0
        return ERROR_INT("&sum not defined", __func__, 1);
319
0
    *psum = 0.0;
320
0
    if (!kel)
321
0
        return ERROR_INT("kernel not defined", __func__, 1);
322
323
0
    kernelGetParameters(kel, &sy, &sx, NULL, NULL);
324
0
    for (i = 0; i < sy; i++) {
325
0
        for (j = 0; j < sx; j++) {
326
0
            *psum += kel->data[i][j];
327
0
        }
328
0
    }
329
0
    return 0;
330
0
}
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
0
{
346
0
l_int32    sx, sy, i, j;
347
0
l_float32  val, minval, maxval;
348
349
0
    if (!pmin && !pmax)
350
0
        return ERROR_INT("neither &min nor &max defined", __func__, 1);
351
0
    if (pmin) *pmin = 0.0;
352
0
    if (pmax) *pmax = 0.0;
353
0
    if (!kel)
354
0
        return ERROR_INT("kernel not defined", __func__, 1);
355
356
0
    kernelGetParameters(kel, &sy, &sx, NULL, NULL);
357
0
    minval = 10000000.0;
358
0
    maxval = -10000000.0;
359
0
    for (i = 0; i < sy; i++) {
360
0
        for (j = 0; j < sx; j++) {
361
0
            val = kel->data[i][j];
362
0
            if (val < minval)
363
0
                minval = val;
364
0
            if (val > maxval)
365
0
                maxval = val;
366
0
        }
367
0
    }
368
0
    if (pmin)
369
0
        *pmin = minval;
370
0
    if (pmax)
371
0
        *pmax = maxval;
372
373
0
    return 0;
374
0
}
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
0
{
399
0
l_int32    i, j, sx, sy, cx, cy;
400
0
l_float32  sum, factor;
401
0
L_KERNEL  *keld;
402
403
0
    if (!kels)
404
0
        return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL);
405
406
0
    kernelGetSum(kels, &sum);
407
0
    if (L_ABS(sum) < 0.00001) {
408
0
        L_WARNING("null sum; not normalizing; returning a copy\n", __func__);
409
0
        return kernelCopy(kels);
410
0
    }
411
412
0
    kernelGetParameters(kels, &sy, &sx, &cy, &cx);
413
0
    if ((keld = kernelCreate(sy, sx)) == NULL)
414
0
        return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL);
415
0
    keld->cy = cy;
416
0
    keld->cx = cx;
417
418
0
    factor = normsum / sum;
419
0
    for (i = 0; i < sy; i++)
420
0
        for (j = 0; j < sx; j++)
421
0
            keld->data[i][j] = factor * kels->data[i][j];
422
423
0
    return keld;
424
0
}
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
0
{
442
0
l_int32    i, j, sx, sy, cx, cy;
443
0
L_KERNEL  *keld;
444
445
0
    if (!kels)
446
0
        return (L_KERNEL *)ERROR_PTR("kels not defined", __func__, NULL);
447
448
0
    kernelGetParameters(kels, &sy, &sx, &cy, &cx);
449
0
    if ((keld = kernelCreate(sy, sx)) == NULL)
450
0
        return (L_KERNEL *)ERROR_PTR("keld not made", __func__, NULL);
451
0
    keld->cy = sy - 1 - cy;
452
0
    keld->cx = sx - 1 - cx;
453
454
0
    for (i = 0; i < sy; i++)
455
0
        for (j = 0; j < sx; j++)
456
0
            keld->data[i][j] = kels->data[sy - 1 - i][sx - 1 - j];
457
458
0
    return keld;
459
0
}
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
0
{
484
0
l_int32      i;
485
0
l_float32  **array;
486
487
0
    if (sx <= 0 || sx > MaxArraySize)
488
0
        return (l_float32 **)ERROR_PTR("sx out of bounds", __func__, NULL);
489
0
    if (sy <= 0 || sy > MaxArraySize)
490
0
        return (l_float32 **)ERROR_PTR("sy out of bounds", __func__, NULL);
491
492
0
    array = (l_float32 **)LEPT_CALLOC(sy, sizeof(l_float32 *));
493
0
    for (i = 0; i < sy; i++)
494
0
        array[i] = (l_float32 *)LEPT_CALLOC(sx, sizeof(l_float32));
495
0
    return array;
496
0
}
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
0
{
511
0
FILE      *fp;
512
0
L_KERNEL  *kel;
513
514
0
    if (!fname)
515
0
        return (L_KERNEL *)ERROR_PTR("fname not defined", __func__, NULL);
516
517
0
    if ((fp = fopenReadStream(fname)) == NULL)
518
0
        return (L_KERNEL *)ERROR_PTR_1("stream not opened",
519
0
                                       fname, __func__, NULL);
520
0
    if ((kel = kernelReadStream(fp)) == NULL) {
521
0
        fclose(fp);
522
0
        return (L_KERNEL *)ERROR_PTR_1("kel not returned",
523
0
                                       fname, __func__, NULL);
524
0
    }
525
0
    fclose(fp);
526
527
0
    return kel;
528
0
}
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
0
{
540
0
l_int32    sy, sx, cy, cx, i, j, ret, version, ignore;
541
0
L_KERNEL  *kel;
542
543
0
    if (!fp)
544
0
        return (L_KERNEL *)ERROR_PTR("stream not defined", __func__, NULL);
545
546
0
    ret = fscanf(fp, "  Kernel Version %d\n", &version);
547
0
    if (ret != 1)
548
0
        return (L_KERNEL *)ERROR_PTR("not a kernel file", __func__, NULL);
549
0
    if (version != KERNEL_VERSION_NUMBER)
550
0
        return (L_KERNEL *)ERROR_PTR("invalid kernel version", __func__, NULL);
551
552
0
    if (fscanf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n",
553
0
            &sy, &sx, &cy, &cx) != 4)
554
0
        return (L_KERNEL *)ERROR_PTR("dimensions not read", __func__, NULL);
555
0
    if (sx > MaxArraySize || sy > MaxArraySize) {
556
0
        L_ERROR("sx = %d or sy = %d > %d\n", __func__, sx, sy, MaxArraySize);
557
0
        return NULL;
558
0
    }
559
0
    if ((kel = kernelCreate(sy, sx)) == NULL)
560
0
        return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL);
561
0
    kernelSetOrigin(kel, cy, cx);
562
563
0
    for (i = 0; i < sy; i++) {
564
0
        for (j = 0; j < sx; j++)
565
0
            ignore = fscanf(fp, "%15f", &kel->data[i][j]);
566
0
        ignore = fscanf(fp, "\n");
567
0
    }
568
0
    ignore = fscanf(fp, "\n");
569
570
0
    return kel;
571
0
}
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
0
{
585
0
FILE  *fp;
586
587
0
    if (!fname)
588
0
        return ERROR_INT("fname not defined", __func__, 1);
589
0
    if (!kel)
590
0
        return ERROR_INT("kel not defined", __func__, 1);
591
592
0
    if ((fp = fopenWriteStream(fname, "wb")) == NULL)
593
0
        return ERROR_INT_1("stream not opened", fname, __func__, 1);
594
0
    kernelWriteStream(fp, kel);
595
0
    fclose(fp);
596
597
0
    return 0;
598
0
}
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
0
{
612
0
l_int32  sx, sy, cx, cy, i, j;
613
614
0
    if (!fp)
615
0
        return ERROR_INT("stream not defined", __func__, 1);
616
0
    if (!kel)
617
0
        return ERROR_INT("kel not defined", __func__, 1);
618
0
    kernelGetParameters(kel, &sy, &sx, &cy, &cx);
619
620
0
    fprintf(fp, "  Kernel Version %d\n", KERNEL_VERSION_NUMBER);
621
0
    fprintf(fp, "  sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx);
622
0
    for (i = 0; i < sy; i++) {
623
0
        for (j = 0; j < sx; j++)
624
0
            fprintf(fp, "%15.4f", kel->data[i][j]);
625
0
        fprintf(fp, "\n");
626
0
    }
627
0
    fprintf(fp, "\n");
628
629
0
    return 0;
630
0
}
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
0
{
665
0
l_int32    n, i, j, index;
666
0
l_float32  val;
667
0
L_KERNEL  *kel;
668
0
NUMA      *na;
669
670
0
    if (h < 1)
671
0
        return (L_KERNEL *)ERROR_PTR("height must be > 0", __func__, NULL);
672
0
    if (w < 1)
673
0
        return (L_KERNEL *)ERROR_PTR("width must be > 0", __func__, NULL);
674
0
    if (cy < 0 || cy >= h)
675
0
        return (L_KERNEL *)ERROR_PTR("cy invalid", __func__, NULL);
676
0
    if (cx < 0 || cx >= w)
677
0
        return (L_KERNEL *)ERROR_PTR("cx invalid", __func__, NULL);
678
679
0
    kel = kernelCreate(h, w);
680
0
    kernelSetOrigin(kel, cy, cx);
681
0
    na = parseStringForNumbers(kdata, " \t\n");
682
0
    n = numaGetCount(na);
683
0
    if (n != w * h) {
684
0
        kernelDestroy(&kel);
685
0
        numaDestroy(&na);
686
0
        lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n);
687
0
        return (L_KERNEL *)ERROR_PTR("invalid integer data", __func__, NULL);
688
0
    }
689
690
0
    index = 0;
691
0
    for (i = 0; i < h; i++) {
692
0
        for (j = 0; j < w; j++) {
693
0
            numaGetFValue(na, index, &val);
694
0
            kernelSetElement(kel, i, j, val);
695
0
            index++;
696
0
        }
697
0
    }
698
699
0
    numaDestroy(&na);
700
0
    return kel;
701
0
}
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
0
{
745
0
char      *filestr, *line;
746
0
l_int32    nlines, i, j, first, index, w, h, cx, cy, n;
747
0
l_float32  val;
748
0
size_t     size;
749
0
NUMA      *na, *nat;
750
0
SARRAY    *sa;
751
0
L_KERNEL  *kel;
752
753
0
    if (!filename)
754
0
        return (L_KERNEL *)ERROR_PTR("filename not defined", __func__, NULL);
755
756
0
    if ((filestr = (char *)l_binaryRead(filename, &size)) == NULL)
757
0
        return (L_KERNEL *)ERROR_PTR_1("file not found",
758
0
                                       filename, __func__, NULL);
759
0
    if (size == 0) {
760
0
        LEPT_FREE(filestr);
761
0
        return (L_KERNEL *)ERROR_PTR_1("file is empty",
762
0
                                       filename, __func__, NULL);
763
0
    }
764
765
0
    sa = sarrayCreateLinesFromString(filestr, 1);
766
0
    LEPT_FREE(filestr);
767
0
    nlines = sarrayGetCount(sa);
768
769
        /* Find the first data line. */
770
0
    for (i = 0, first = 0; i < nlines; i++) {
771
0
        line = sarrayGetString(sa, i, L_NOCOPY);
772
0
        if (line[0] != '#') {
773
0
            first = i;
774
0
            break;
775
0
        }
776
0
    }
777
778
        /* Find the kernel dimensions and origin location. */
779
0
    line = sarrayGetString(sa, first, L_NOCOPY);
780
0
    if (sscanf(line, "%d %d", &h, &w) != 2) {
781
0
        sarrayDestroy(&sa);
782
0
        return (L_KERNEL *)ERROR_PTR("error reading h,w", __func__, NULL);
783
0
    }
784
0
    if (h > MaxArraySize || w > MaxArraySize) {
785
0
        L_ERROR("h = %d or w = %d > %d\n", __func__, h, w, MaxArraySize);
786
0
        sarrayDestroy(&sa);
787
0
        return NULL;
788
0
    }
789
0
    line = sarrayGetString(sa, first + 1, L_NOCOPY);
790
0
    if (sscanf(line, "%d %d", &cy, &cx) != 2) {
791
0
        sarrayDestroy(&sa);
792
0
        return (L_KERNEL *)ERROR_PTR("error reading cy,cx", __func__, NULL);
793
0
    }
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
0
    na = numaCreate(0);
799
0
    for (i = first + 2; i < nlines; i++) {
800
0
        line = sarrayGetString(sa, i, L_NOCOPY);
801
0
        if (line[0] == '\0' || line[0] == '\n' || line[0] == '#')
802
0
            break;
803
0
        nat = parseStringForNumbers(line, " \t\n");
804
0
        numaJoin(na, nat, 0, -1);
805
0
        numaDestroy(&nat);
806
0
    }
807
0
    sarrayDestroy(&sa);
808
809
0
    n = numaGetCount(na);
810
0
    if (n != w * h) {
811
0
        numaDestroy(&na);
812
0
        lept_stderr("w = %d, h = %d, num ints = %d\n", w, h, n);
813
0
        return (L_KERNEL *)ERROR_PTR("invalid integer data", __func__, NULL);
814
0
    }
815
816
0
    kel = kernelCreate(h, w);
817
0
    kernelSetOrigin(kel, cy, cx);
818
0
    index = 0;
819
0
    for (i = 0; i < h; i++) {
820
0
        for (j = 0; j < w; j++) {
821
0
            numaGetFValue(na, index, &val);
822
0
            kernelSetElement(kel, i, j, val);
823
0
            index++;
824
0
        }
825
0
    }
826
827
0
    numaDestroy(&na);
828
0
    return kel;
829
0
}
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
0
{
852
0
l_int32    i, j, w, h, d;
853
0
l_uint32   val;
854
0
L_KERNEL  *kel;
855
856
0
    if (!pix)
857
0
        return (L_KERNEL *)ERROR_PTR("pix not defined", __func__, NULL);
858
0
    pixGetDimensions(pix, &w, &h, &d);
859
0
    if (d != 8)
860
0
        return (L_KERNEL *)ERROR_PTR("pix not 8 bpp", __func__, NULL);
861
0
    if (cy < 0 || cx < 0 || cy >= h || cx >= w)
862
0
        return (L_KERNEL *)ERROR_PTR("(cy, cx) invalid", __func__, NULL);
863
864
0
    kel = kernelCreate(h, w);
865
0
    kernelSetOrigin(kel, cy, cx);
866
0
    for (i = 0; i < h; i++) {
867
0
        for (j = 0; j < w; j++) {
868
0
            pixGetPixel(pix, j, i, &val);
869
0
            kernelSetElement(kel, i, j, (l_float32)val);
870
0
        }
871
0
    }
872
873
0
    return kel;
874
0
}
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
0
{
911
0
l_int32    i, j, w, h, sx, sy, cx, cy, width, x0, y0;
912
0
l_int32    normval;
913
0
l_float32  minval, maxval, max, val, norm;
914
0
PIX       *pixd, *pixt0, *pixt1;
915
916
0
    if (!kel)
917
0
        return (PIX *)ERROR_PTR("kernel not defined", __func__, NULL);
918
919
        /* Normalize the max value to be 255 for display */
920
0
    kernelGetParameters(kel, &sy, &sx, &cy, &cx);
921
0
    kernelGetMinMax(kel, &minval, &maxval);
922
0
    max = L_MAX(maxval, -minval);
923
0
    if (max == 0.0)
924
0
        return (PIX *)ERROR_PTR("kernel elements all 0.0", __func__, NULL);
925
0
    norm = 255.f / (l_float32)max;
926
927
        /* Handle the 1 element/pixel case; typically with large kernels */
928
0
    if (size == 1 && gthick == 0) {
929
0
        pixd = pixCreate(sx, sy, 8);
930
0
        for (i = 0; i < sy; i++) {
931
0
            for (j = 0; j < sx; j++) {
932
0
                kernelGetElement(kel, i, j, &val);
933
0
                normval = (l_int32)(norm * L_ABS(val));
934
0
                pixSetPixel(pixd, j, i, normval);
935
0
            }
936
0
        }
937
0
        return pixd;
938
0
    }
939
940
        /* Enforce the constraints for the grid line version */
941
0
    if (size < 17) {
942
0
        L_WARNING("size < 17; setting to 17\n", __func__);
943
0
        size = 17;
944
0
    }
945
0
    if (size % 2 == 0)
946
0
        size++;
947
0
    if (gthick < 2) {
948
0
        L_WARNING("grid thickness < 2; setting to 2\n", __func__);
949
0
        gthick = 2;
950
0
    }
951
952
0
    w = size * sx + gthick * (sx + 1);
953
0
    h = size * sy + gthick * (sy + 1);
954
0
    pixd = pixCreate(w, h, 8);
955
956
        /* Generate grid lines */
957
0
    for (i = 0; i <= sy; i++)
958
0
        pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick),
959
0
                      w - 1, gthick / 2 + i * (size + gthick),
960
0
                      gthick, L_SET_PIXELS);
961
0
    for (j = 0; j <= sx; j++)
962
0
        pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0,
963
0
                      gthick / 2 + j * (size + gthick), h - 1,
964
0
                      gthick, L_SET_PIXELS);
965
966
        /* Generate mask for each element */
967
0
    pixt0 = pixCreate(size, size, 1);
968
0
    pixSetAll(pixt0);
969
970
        /* Generate crossed lines for origin pattern */
971
0
    pixt1 = pixCreate(size, size, 1);
972
0
    width = size / 8;
973
0
    pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size),
974
0
                           size / 2, (l_int32)(0.88 * size),
975
0
                           width, L_SET_PIXELS);
976
0
    pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2,
977
0
                           (l_int32)(0.85 * size), size / 2,
978
0
                           width, L_FLIP_PIXELS);
979
0
    pixRasterop(pixt1, size / 2 - width, size / 2 - width,
980
0
                2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0);
981
982
        /* Paste the patterns in */
983
0
    y0 = gthick;
984
0
    for (i = 0; i < sy; i++) {
985
0
        x0 = gthick;
986
0
        for (j = 0; j < sx; j++) {
987
0
            kernelGetElement(kel, i, j, &val);
988
0
            normval = (l_int32)(norm * L_ABS(val));
989
0
            pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0);
990
0
            if (i == cy && j == cx)
991
0
                pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval);
992
0
            x0 += size + gthick;
993
0
        }
994
0
        y0 += size + gthick;
995
0
    }
996
997
0
    pixDestroy(&pixt0);
998
0
    pixDestroy(&pixt1);
999
0
    return pixd;
1000
0
}
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
0
{
1022
0
char      *newstr, *head;
1023
0
char      *tail = NULL;
1024
0
l_float32  val;
1025
0
NUMA      *na;
1026
1027
0
    if (!str)
1028
0
        return (NUMA *)ERROR_PTR("str not defined", __func__, NULL);
1029
1030
0
    newstr = stringNew(str);  /* to enforce const-ness of str */
1031
0
    na = numaCreate(0);
1032
0
    head = strtokSafe(newstr, seps, &tail);
1033
0
    val = atof(head);
1034
0
    numaAddNumber(na, val);
1035
0
    LEPT_FREE(head);
1036
0
    while ((head = strtokSafe(NULL, seps, &tail)) != NULL) {
1037
0
        val = atof(head);
1038
0
        numaAddNumber(na, val);
1039
0
        LEPT_FREE(head);
1040
0
    }
1041
1042
0
    LEPT_FREE(newstr);
1043
0
    return na;
1044
0
}
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
0
{
1074
0
l_int32    i, j;
1075
0
l_float32  normval;
1076
0
L_KERNEL  *kel;
1077
1078
0
    if ((kel = kernelCreate(height, width)) == NULL)
1079
0
        return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL);
1080
0
    kernelSetOrigin(kel, cy, cx);
1081
0
    normval = 1.0f / (l_float32)(height * width);
1082
0
    for (i = 0; i < height; i++) {
1083
0
        for (j = 0; j < width; j++) {
1084
0
            kernelSetElement(kel, i, j, normval);
1085
0
        }
1086
0
    }
1087
1088
0
    return kel;
1089
0
}
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
0
{
1118
0
l_int32    sx, sy, i, j;
1119
0
l_float32  val;
1120
0
L_KERNEL  *kel;
1121
1122
0
    sx = 2 * halfw + 1;
1123
0
    sy = 2 * halfh + 1;
1124
0
    if ((kel = kernelCreate(sy, sx)) == NULL)
1125
0
        return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL);
1126
0
    kernelSetOrigin(kel, halfh, halfw);
1127
0
    for (i = 0; i < sy; i++) {
1128
0
        for (j = 0; j < sx; j++) {
1129
0
            val = expf(-(l_float32)((i - halfh) * (i - halfh) +
1130
0
                                    (j - halfw) * (j - halfw)) /
1131
0
                        (2. * stdev * stdev));
1132
0
            kernelSetElement(kel, i, j, max * val);
1133
0
        }
1134
0
    }
1135
1136
0
    return kel;
1137
0
}
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
0
{
1173
0
    if (!pkelx || !pkely)
1174
0
        return ERROR_INT("&kelx and &kely not defined", __func__, 1);
1175
1176
0
    *pkelx = makeGaussianKernel(0, halfw, stdev, max);
1177
0
    *pkely = makeGaussianKernel(halfh, 0, stdev, 1.0);
1178
0
    return 0;
1179
0
}
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
0
{
1215
0
l_int32    sx, sy, i, j;
1216
0
l_float32  pi, squaredist, highnorm, lownorm, val;
1217
0
L_KERNEL  *kel;
1218
1219
0
    sx = 2 * halfw + 1;
1220
0
    sy = 2 * halfh + 1;
1221
0
    if ((kel = kernelCreate(sy, sx)) == NULL)
1222
0
        return (L_KERNEL *)ERROR_PTR("kel not made", __func__, NULL);
1223
0
    kernelSetOrigin(kel, halfh, halfw);
1224
1225
0
    pi = 3.1415926535f;
1226
0
    for (i = 0; i < sy; i++) {
1227
0
        for (j = 0; j < sx; j++) {
1228
0
            squaredist = (l_float32)((i - halfh) * (i - halfh) +
1229
0
                                     (j - halfw) * (j - halfw));
1230
0
            highnorm = 1.f / (2 * stdev * stdev);
1231
0
            lownorm = highnorm / (ratio * ratio);
1232
0
            val = (highnorm / pi) * expf(-(highnorm * squaredist))
1233
0
                  - (lownorm / pi) * expf(-(lownorm * squaredist));
1234
0
            kernelSetElement(kel, i, j, val);
1235
0
        }
1236
0
    }
1237
1238
0
    return kel;
1239
0
}