Coverage Report

Created: 2025-06-13 07:15

/src/leptonica/src/pixtiling.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
 * \file  pixtiling.c
29
 * <pre>
30
 *
31
 *        PIXTILING       *pixTilingCreate()
32
 *        void            *pixTilingDestroy()
33
 *        l_int32          pixTilingGetCount()
34
 *        l_int32          pixTilingGetSize()
35
 *        PIX             *pixTilingGetTile()
36
 *        l_int32          pixTilingNoStripOnPaint()
37
 *        l_int32          pixTilingPaintTile()
38
 *
39
 *   This provides a simple way to split an image into tiles
40
 *   and to perform operations independently on each tile.
41
 *
42
 *   The tile created with pixTilingGetTile() can have pixels in
43
 *   adjacent tiles for computation.  The number of extra pixels
44
 *   on each side of the tile is given by an 'overlap' parameter
45
 *   to pixTilingCreate().  For tiles at the boundary of
46
 *   the input image, quasi-overlap pixels are created by reflection
47
 *   symmetry into the tile.
48
 *
49
 *   Here's a typical intended usage.  Suppose you want to parallelize
50
 *   the operation on an image, by operating on tiles.  For each
51
 *   tile, you want to generate an in-place image result at the same
52
 *   resolution.  Suppose you choose a one-dimensional vertical tiling,
53
 *   where the desired tile width is 256 pixels and the overlap is
54
 *   30 pixels on left and right sides:
55
 *
56
 *     PIX *pixd = pixCreateTemplate(pixs);  // output
57
 *     PIXTILING  *pt = pixTilingCreate(pixs, 0, 1, 256, 30, 0);
58
 *     pixTilingGetCount(pt, &nx, NULL);
59
 *     for (j = 0; j < nx; j++) {
60
 *         PIX *pixt = pixTilingGetTile(pt, 0, j);
61
 *         SomeInPlaceOperation(pixt, 30, 0, ...);
62
 *         pixTilingPaintTile(pixd, 0, j, pixt, pt);
63
 *         pixDestroy(&pixt);
64
 *     }
65
 *
66
 *   In this example, note the following:
67
 *    ~ The unspecfified in-place operation could instead generate
68
 *      a new pix.  If this is done, the resulting pix must be the
69
 *      same size as pixt, because pixTilingPaintTile() makes that
70
 *      assumption, removing the overlap pixels before painting
71
 *      into the destination.
72
 *    ~ The 'overlap' parameters have been included in your function,
73
 *      to indicate which pixels are not in the exterior overlap region.
74
 *      You will need to change only pixels that are not in the overlap
75
 *      region, because those are the pixels that will be painted
76
 *      into the destination.
77
 *    ~ For tiles on the outside of the image, mirrored pixels are
78
 *      added to substitute for the overlap that is added to interior
79
 *      tiles.  This allows you to implement your function without
80
 *      reference to which tile it is; no special coding is necessary
81
 *      for pixels that are near the image boundary.
82
 *    ~ The tiles are labeled by (i, j) = (row, column),
83
 *      and in this example there is one row and nx columns.
84
 * </pre>
85
 */
86
87
#ifdef HAVE_CONFIG_H
88
#include <config_auto.h>
89
#endif  /* HAVE_CONFIG_H */
90
91
#include "allheaders.h"
92
#include "pix_internal.h"
93
94
/*!
95
 * \brief   pixTilingCreate()
96
 *
97
 * \param[in]    pixs       pix to be tiled; any depth; colormap OK
98
 * \param[in]    nx         number of tiles across image
99
 * \param[in]    ny         number of tiles down image
100
 * \param[in]    w          desired width of each tile
101
 * \param[in]    h          desired height of each tile
102
 * \param[in]    xoverlap   overlap into neighboring tiles on each side
103
 * \param[in]    yoverlap   overlap into neighboring tiles above and below
104
 * \return  pixtiling, or NULL on error
105
 *
106
 * <pre>
107
 * Notes:
108
 *      (1) We put a clone of pixs in the PixTiling.
109
 *      (2) The input to pixTilingCreate() for horizontal tiling can be
110
 *          either the number of tiles across the image or the approximate
111
 *          width of the tiles.  If the latter, the actual width will be
112
 *          determined by making all tiles but the last of equal width, and
113
 *          making the last as close to the others as possible.  The same
114
 *          consideration is applied independently to the vertical tiling.
115
 *          To specify tile width, set nx = 0; to specify the number of
116
 *          tiles horizontally across the image, set w = 0.
117
 *      (3) If pixs is to be tiled in one-dimensional strips, use ny = 1 for
118
 *          vertical strips and nx = 1 for horizontal strips.
119
 *      (4) The overlap must not be larger than the width or height of
120
 *          the leftmost or topmost tile(s).
121
 * </pre>
122
 */
123
PIXTILING *
124
pixTilingCreate(PIX     *pixs,
125
                l_int32  nx,
126
                l_int32  ny,
127
                l_int32  w,
128
                l_int32  h,
129
                l_int32  xoverlap,
130
                l_int32  yoverlap)
131
0
{
132
0
l_int32     width, height;
133
0
PIXTILING  *pt;
134
135
0
    if (!pixs)
136
0
        return (PIXTILING *)ERROR_PTR("pixs not defined", __func__, NULL);
137
0
    if (nx < 1 && w < 1)
138
0
        return (PIXTILING *)ERROR_PTR("invalid width spec", __func__, NULL);
139
0
    if (ny < 1 && h < 1)
140
0
        return (PIXTILING *)ERROR_PTR("invalid height spec", __func__, NULL);
141
142
        /* Find the tile width and number of tiles.  All tiles except the
143
         * rightmost ones have the same width.  The width of the
144
         * rightmost ones are at least the width of the others and
145
         * less than twice that width.  Ditto for tile height. */
146
0
    pixGetDimensions(pixs, &width, &height, NULL);
147
0
    if (nx == 0)
148
0
        nx = L_MAX(1, width / w);
149
0
    w = width / nx;  /* possibly reset */
150
0
    if (ny == 0)
151
0
        ny = L_MAX(1, height / h);
152
0
    h = height / ny;  /* possibly reset */
153
0
    if (xoverlap > w || yoverlap > h) {
154
0
        L_INFO("tile width = %d, tile height = %d\n", __func__, w, h);
155
0
        return (PIXTILING *)ERROR_PTR("overlap too large", __func__, NULL);
156
0
    }
157
158
0
    pt = (PIXTILING *)LEPT_CALLOC(1, sizeof(PIXTILING));
159
0
    pt->pix = pixClone(pixs);
160
0
    pt->xoverlap = xoverlap;
161
0
    pt->yoverlap = yoverlap;
162
0
    pt->nx = nx;
163
0
    pt->ny = ny;
164
0
    pt->w = w;
165
0
    pt->h = h;
166
0
    pt->strip = TRUE;
167
0
    return pt;
168
0
}
169
170
171
/*!
172
 * \brief   pixTilingDestroy()
173
 *
174
 * \param[in,out]   ppt   will be set to null before returning
175
 * \return  void
176
 */
177
void
178
pixTilingDestroy(PIXTILING  **ppt)
179
0
{
180
0
PIXTILING  *pt;
181
182
0
    if (ppt == NULL) {
183
0
        L_WARNING("ptr address is null!\n", __func__);
184
0
        return;
185
0
    }
186
187
0
    if ((pt = *ppt) == NULL)
188
0
        return;
189
190
0
    pixDestroy(&pt->pix);
191
0
    LEPT_FREE(pt);
192
0
    *ppt = NULL;
193
0
}
194
195
196
/*!
197
 * \brief   pixTilingGetCount()
198
 *
199
 * \param[in]    pt     pixtiling
200
 * \param[out]   pnx    [optional] nx; can be null
201
 * \param[out]   pny    [optional] ny; can be null
202
 * \return  0 if OK, 1 on error
203
 */
204
l_ok
205
pixTilingGetCount(PIXTILING  *pt,
206
                  l_int32    *pnx,
207
                  l_int32    *pny)
208
0
{
209
0
    if (!pt)
210
0
        return ERROR_INT("pt not defined", __func__, 1);
211
0
    if (pnx) *pnx = pt->nx;
212
0
    if (pny) *pny = pt->ny;
213
0
    return 0;
214
0
}
215
216
217
/*!
218
 * \brief   pixTilingGetSize()
219
 *
220
 * \param[in]    pt    pixtiling
221
 * \param[out]   pw    [optional] tile width; can be null
222
 * \param[out]   ph    [optional] tile height; can be null
223
 * \return  0 if OK, 1 on error
224
 */
225
l_ok
226
pixTilingGetSize(PIXTILING  *pt,
227
                 l_int32    *pw,
228
                 l_int32    *ph)
229
0
{
230
0
    if (!pt)
231
0
        return ERROR_INT("pt not defined", __func__, 1);
232
0
    if (pw) *pw = pt->w;
233
0
    if (ph) *ph = pt->h;
234
0
    return 0;
235
0
}
236
237
238
/*!
239
 * \brief   pixTilingGetTile()
240
 *
241
 * \param[in]    pt    pixtiling
242
 * \param[in]    i     tile row index
243
 * \param[in]    j     tile column index
244
 * \return  pixd   tile with appropriate boundary (overlap) pixels added,
245
 *                    or NULL on error
246
 */
247
PIX *
248
pixTilingGetTile(PIXTILING  *pt,
249
                 l_int32     i,
250
                 l_int32     j)
251
0
{
252
0
l_int32  wpix, hpix, wt, ht, nx, ny;
253
0
l_int32  xoverlap, yoverlap, wtlast, htlast;
254
0
l_int32  left, top, xtraleft, xtraright, xtratop, xtrabot, width, height;
255
0
BOX     *box;
256
0
PIX     *pixs, *pixt, *pixd;
257
258
0
    if (!pt)
259
0
        return (PIX *)ERROR_PTR("pt not defined", __func__, NULL);
260
0
    if ((pixs = pt->pix) == NULL)
261
0
        return (PIX *)ERROR_PTR("pix not found", __func__, NULL);
262
0
    pixTilingGetCount(pt, &nx, &ny);
263
0
    if (i < 0 || i >= ny)
264
0
        return (PIX *)ERROR_PTR("invalid row index i", __func__, NULL);
265
0
    if (j < 0 || j >= nx)
266
0
        return (PIX *)ERROR_PTR("invalid column index j", __func__, NULL);
267
268
        /* Grab the tile with as much overlap as exists within the
269
         * input pix.   First, compute the (left, top) coordinates.  */
270
0
    pixGetDimensions(pixs, &wpix, &hpix, NULL);
271
0
    pixTilingGetSize(pt, &wt, &ht);
272
0
    xoverlap = pt->xoverlap;
273
0
    yoverlap = pt->yoverlap;
274
0
    wtlast = wpix - wt * (nx - 1);
275
0
    htlast = hpix - ht * (ny - 1);
276
0
    left = L_MAX(0, j * wt - xoverlap);
277
0
    top = L_MAX(0, i * ht - yoverlap);
278
279
        /* Get the width and height of the tile, including whatever
280
         * overlap is available. */
281
0
    if (nx == 1)
282
0
        width = wpix;
283
0
    else if (j == 0)
284
0
        width = wt + xoverlap;
285
0
    else if (j == nx - 1)
286
0
        width = wtlast + xoverlap;
287
0
    else
288
0
        width = wt + 2 * xoverlap;
289
290
0
    if (ny == 1)
291
0
        height = hpix;
292
0
    else if (i == 0)
293
0
        height = ht + yoverlap;
294
0
    else if (i == ny - 1)
295
0
        height = htlast + yoverlap;
296
0
    else
297
0
        height = ht + 2 * yoverlap;
298
0
    box = boxCreate(left, top, width, height);
299
0
    pixt = pixClipRectangle(pixs, box, NULL);
300
0
    boxDestroy(&box);
301
302
        /* If no overlap, do not add any special case borders */
303
0
    if (xoverlap == 0 && yoverlap == 0)
304
0
        return pixt;
305
306
        /* Add overlap as a mirrored border, in the 8 special cases where
307
         * the tile touches the border of the input pix.  The xtratop (etc)
308
         * parameters are required where the tile is either full width
309
         * or full height.  */
310
0
    xtratop = xtrabot = xtraleft = xtraright = 0;
311
0
    if (nx == 1)
312
0
        xtraleft = xtraright = xoverlap;
313
0
    if (ny == 1)
314
0
        xtratop = xtrabot = yoverlap;
315
0
    if (i == 0 && j == 0)
316
0
        pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
317
0
                                    yoverlap, xtrabot);
318
0
    else if (i == 0 && j == nx - 1)
319
0
        pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
320
0
                                    yoverlap, xtrabot);
321
0
    else if (i == ny - 1 && j == 0)
322
0
        pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright,
323
0
                                    xtratop, yoverlap);
324
0
    else if (i == ny - 1 && j == nx - 1)
325
0
        pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap,
326
0
                                    xtratop, yoverlap);
327
0
    else if (i == 0)
328
0
        pixd = pixAddMirroredBorder(pixt, 0, 0, yoverlap, xtrabot);
329
0
    else if (i == ny - 1)
330
0
        pixd = pixAddMirroredBorder(pixt, 0, 0, xtratop, yoverlap);
331
0
    else if (j == 0)
332
0
        pixd = pixAddMirroredBorder(pixt, xoverlap, xtraright, 0, 0);
333
0
    else if (j == nx - 1)
334
0
        pixd = pixAddMirroredBorder(pixt, xtraleft, xoverlap, 0, 0);
335
0
    else
336
0
        pixd = pixClone(pixt);
337
0
    pixDestroy(&pixt);
338
339
0
    return pixd;
340
0
}
341
342
343
/*!
344
 * \brief   pixTilingNoStripOnPaint()
345
 *
346
 * \param[in]    pt    pixtiling
347
 * \return  0 if OK, 1 on error
348
 *
349
 * <pre>
350
 * Notes:
351
 *      (1) The default for paint is to strip out the overlap pixels
352
 *          that are added by pixTilingGetTile().  However, some
353
 *          operations will generate an image with these pixels
354
 *          stripped off.  This tells the paint operation not
355
 *          to strip the added boundary pixels when painting.
356
 * </pre>
357
 */
358
l_ok
359
pixTilingNoStripOnPaint(PIXTILING  *pt)
360
0
{
361
0
    if (!pt)
362
0
        return ERROR_INT("pt not defined", __func__, 1);
363
0
    pt->strip = FALSE;
364
0
    return 0;
365
0
}
366
367
368
/*!
369
 * \brief   pixTilingPaintTile()
370
 *
371
 * \param[in]    pixd    dest: paint tile onto this, without overlap
372
 * \param[in]    i       tile row index
373
 * \param[in]    j       tile column index
374
 * \param[in]    pixs    source: tile to be painted from
375
 * \param[in]    pt      pixtiling struct
376
 * \return  0 if OK, 1 on error
377
 */
378
l_ok
379
pixTilingPaintTile(PIX        *pixd,
380
                   l_int32     i,
381
                   l_int32     j,
382
                   PIX        *pixs,
383
                   PIXTILING  *pt)
384
0
{
385
0
l_int32  w, h;
386
387
0
    if (!pixd)
388
0
        return ERROR_INT("pixd not defined", __func__, 1);
389
0
    if (!pixs)
390
0
        return ERROR_INT("pixs not defined", __func__, 1);
391
0
    if (!pt)
392
0
        return ERROR_INT("pt not defined", __func__, 1);
393
0
    if (i < 0 || i >= pt->ny)
394
0
        return ERROR_INT("invalid row index i", __func__, 1);
395
0
    if (j < 0 || j >= pt->nx)
396
0
        return ERROR_INT("invalid column index j", __func__, 1);
397
398
        /* Strip added border pixels off if requested */
399
0
    pixGetDimensions(pixs, &w, &h, NULL);
400
0
    if (pt->strip == TRUE) {
401
0
        pixRasterop(pixd, j * pt->w, i * pt->h,
402
0
                    w - 2 * pt->xoverlap, h - 2 * pt->yoverlap, PIX_SRC,
403
0
                    pixs, pt->xoverlap, pt->yoverlap);
404
0
    } else {
405
0
        pixRasterop(pixd, j * pt->w, i * pt->h, w, h, PIX_SRC, pixs, 0, 0);
406
0
    }
407
408
0
    return 0;
409
0
}