/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  | }  |