comparison mupdf-source/thirdparty/leptonica/src/morphdwa.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 morphdwa.c
29 * <pre>
30 *
31 * Binary morphological (dwa) ops with brick Sels
32 * PIX *pixDilateBrickDwa()
33 * PIX *pixErodeBrickDwa()
34 * PIX *pixOpenBrickDwa()
35 * PIX *pixCloseBrickDwa()
36 *
37 * Binary composite morphological (dwa) ops with brick Sels
38 * PIX *pixDilateCompBrickDwa()
39 * PIX *pixErodeCompBrickDwa()
40 * PIX *pixOpenCompBrickDwa()
41 * PIX *pixCloseCompBrickDwa()
42 *
43 * Binary extended composite morphological (dwa) ops with brick Sels
44 * PIX *pixDilateCompBrickExtendDwa()
45 * PIX *pixErodeCompBrickExtendDwa()
46 * PIX *pixOpenCompBrickExtendDwa()
47 * PIX *pixCloseCompBrickExtendDwa()
48 * l_int32 getExtendedCompositeParameters()
49 *
50 * These are higher-level interfaces for dwa morphology with brick Sels.
51 * Because many morphological operations are performed using
52 * separable brick Sels, it is useful to have a simple interface
53 * for this.
54 *
55 * We have included all 58 of the brick Sels that are generated
56 * by selaAddBasic(). These are sufficient for all the decomposable
57 * bricks up to size 63, which is the limit for dwa Sels with
58 * origins at the center of the Sel.
59 *
60 * All three sets can be used as the basic interface for general
61 * brick operations. Here are the internal calling sequences:
62 *
63 * (1) If you try to apply a non-decomposable operation, such as
64 * pixErodeBrickDwa(), with a Sel size that doesn't exist,
65 * this calls a decomposable operation, pixErodeCompBrickDwa(),
66 * instead. This can differ in linear Sel size by up to
67 * 2 pixels from the request.
68 *
69 * (2) If either Sel brick dimension is greater than 63, the extended
70 * composite function is called.
71 *
72 * (3) The extended composite function calls the composite function
73 * a number of times with size 63, and once with size < 63.
74 * Because each operation with a size of 63 is done compositely
75 * with 7 x 9 (exactly 63), the net result is correct in
76 * length to within 2 pixels.
77 *
78 * For composite operations, both using a comb and extended (beyond 63),
79 * horizontal and vertical operations are composed separately
80 * and sequentially.
81 *
82 * We have also included use of all the 76 comb Sels that are generated
83 * by selaAddDwaCombs(). The generated code is in dwacomb.2.c
84 * and dwacomblow.2.c. These are used for the composite dwa
85 * brick operations.
86 *
87 * The non-composite brick operations, such as pixDilateBrickDwa(),
88 * will call the associated composite operation in situations where
89 * the requisite brick Sel has not been compiled into fmorphgen*.1.c.
90 *
91 * If you want to use brick Sels that are not represented in the
92 * basic set of 58, you must generate the dwa code to implement them.
93 * You have three choices for how to use these:
94 *
95 * (1) Add both the new Sels and the dwa code to the library:
96 * ~ For simplicity, add your new brick Sels to those defined
97 * in selaAddBasic().
98 * ~ Recompile the library.
99 * ~ Make prog/fmorphautogen.
100 * ~ Run prog/fmorphautogen, to generate new versions of the
101 * dwa code in fmorphgen.1.c and fmorphgenlow.1.c.
102 * ~ Copy these two files to src.
103 * ~ Recompile the library again.
104 * ~ Use the new brick Sels in your program and compile it.
105 *
106 * (2) Make both the new Sels and dwa code outside the library,
107 * and link it directly to an executable:
108 * ~ Write a function to generate the new Sels in a Sela, and call
109 * fmorphautogen(sela, <N>, filename) to generate the code.
110 * ~ Compile your program that uses the newly generated function
111 * pixMorphDwa_<N>(), and link to the two new C files.
112 *
113 * (3) Make the new Sels in the library and use the dwa code outside it:
114 * ~ Add code in the library to generate your new brick Sels.
115 * (It is suggested that you NOT add these Sels to the
116 * selaAddBasic() function; write a new function that generates
117 * a new Sela.)
118 * ~ Recompile the library.
119 * ~ Write a small program that generates the Sela and calls
120 * fmorphautogen(sela, <N>, filename) to generate the code.
121 * ~ Compile your program that uses the newly generated function
122 * pixMorphDwa_<N>(), and link to the two new C files.
123 * As an example of this approach, see prog/dwamorph*_reg.c:
124 * ~ added selaAddDwaLinear() to sel2.c
125 * ~ wrote dwamorph1_reg.c, to generate the dwa code.
126 * ~ compiled and linked the generated code with the application,
127 * dwamorph2_reg.c. (Note: because this was a regression test,
128 * dwamorph1_reg also builds and runs the application program.)
129 * </pre>
130 */
131
132 #ifdef HAVE_CONFIG_H
133 #include <config_auto.h>
134 #endif /* HAVE_CONFIG_H */
135
136 #include "allheaders.h"
137
138 #ifndef NO_CONSOLE_IO
139 #define DEBUG_SEL_LOOKUP 0
140 #endif /* ~NO_CONSOLE_IO */
141
142 /*-----------------------------------------------------------------*
143 * Binary morphological (dwa) ops with brick Sels *
144 *-----------------------------------------------------------------*/
145 /*!
146 * \brief pixDilateBrickDwa()
147 *
148 * \param[in] pixd [optional]; this can be null, equal to pixs,
149 * or different from pixs
150 * \param[in] pixs 1 bpp
151 * \param[in] hsize width of brick Sel
152 * \param[in] vsize height of brick Sel
153 * \return pixd
154 *
155 * <pre>
156 * Notes:
157 * (1) These implement 2D brick Sels, using linear Sels generated
158 * with selaAddBasic().
159 * (2) A brick Sel has hits for all elements.
160 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
161 * (4) Do separably if both hsize and vsize are > 1.
162 * (5) It is necessary that both horizontal and vertical Sels
163 * of the input size are defined in the basic sela.
164 * (6) There are three cases:
165 * (a) pixd == null (result into new pixd)
166 * (b) pixd == pixs (in-place; writes result back to pixs)
167 * (c) pixd != pixs (puts result into existing pixd)
168 * (7) For clarity, if the case is known, use these patterns:
169 * (a) pixd = pixDilateBrickDwa(NULL, pixs, ...);
170 * (b) pixDilateBrickDwa(pixs, pixs, ...);
171 * (c) pixDilateBrickDwa(pixd, pixs, ...);
172 * (8) The size of pixd is determined by pixs.
173 * (9) If either linear Sel is not found, this calls
174 * the appropriate decomposible function.
175 * </pre>
176 */
177 PIX *
178 pixDilateBrickDwa(PIX *pixd,
179 PIX *pixs,
180 l_int32 hsize,
181 l_int32 vsize)
182 {
183 l_int32 found;
184 char *selnameh, *selnamev;
185 SELA *sela;
186 PIX *pixt1, *pixt2, *pixt3;
187
188 if (!pixs)
189 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
190 if (pixGetDepth(pixs) != 1)
191 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
192 if (hsize < 1 || vsize < 1)
193 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
194
195 if (hsize == 1 && vsize == 1)
196 return pixCopy(pixd, pixs);
197
198 sela = selaAddBasic(NULL);
199 found = TRUE;
200 selnameh = selnamev = NULL;
201 if (hsize > 1) {
202 selnameh = selaGetBrickName(sela, hsize, 1);
203 if (!selnameh) found = FALSE;
204 }
205 if (vsize > 1) {
206 selnamev = selaGetBrickName(sela, 1, vsize);
207 if (!selnamev) found = FALSE;
208 }
209 selaDestroy(&sela);
210 if (!found) {
211 L_INFO("Calling the decomposable dwa function\n", __func__);
212 if (selnameh) LEPT_FREE(selnameh);
213 if (selnamev) LEPT_FREE(selnamev);
214 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
215 }
216
217 if (vsize == 1) {
218 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnameh);
219 LEPT_FREE(selnameh);
220 } else if (hsize == 1) {
221 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_DILATE, selnamev);
222 LEPT_FREE(selnamev);
223 } else {
224 pixt1 = pixAddBorder(pixs, 32, 0);
225 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
226 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_DILATE, selnamev);
227 pixt2 = pixRemoveBorder(pixt1, 32);
228 pixDestroy(&pixt1);
229 pixDestroy(&pixt3);
230 LEPT_FREE(selnameh);
231 LEPT_FREE(selnamev);
232 }
233
234 if (!pixd)
235 return pixt2;
236
237 pixTransferAllData(pixd, &pixt2, 0, 0);
238 return pixd;
239 }
240
241
242 /*!
243 * \brief pixErodeBrickDwa()
244 *
245 * \param[in] pixd [optional]; this can be null, equal to pixs,
246 * or different from pixs
247 * \param[in] pixs 1 bpp
248 * \param[in] hsize width of brick Sel
249 * \param[in] vsize height of brick Sel
250 * \return pixd
251 *
252 * <pre>
253 * Notes:
254 * (1) These implement 2D brick Sels, using linear Sels generated
255 * with selaAddBasic().
256 * (2) A brick Sel has hits for all elements.
257 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
258 * (4) Do separably if both hsize and vsize are > 1.
259 * (5) It is necessary that both horizontal and vertical Sels
260 * of the input size are defined in the basic sela.
261 * (6) Note that we must always set or clear the border pixels
262 * before each operation, depending on the the b.c.
263 * (symmetric or asymmetric).
264 * (7) There are three cases:
265 * (a) pixd == null (result into new pixd)
266 * (b) pixd == pixs (in-place; writes result back to pixs)
267 * (c) pixd != pixs (puts result into existing pixd)
268 * (8) For clarity, if the case is known, use these patterns:
269 * (a) pixd = pixErodeBrickDwa(NULL, pixs, ...);
270 * (b) pixErodeBrickDwa(pixs, pixs, ...);
271 * (c) pixErodeBrickDwa(pixd, pixs, ...);
272 * (9) The size of the result is determined by pixs.
273 * (10) If either linear Sel is not found, this calls
274 * the appropriate decomposible function.
275 * </pre>
276 */
277 PIX *
278 pixErodeBrickDwa(PIX *pixd,
279 PIX *pixs,
280 l_int32 hsize,
281 l_int32 vsize)
282 {
283 l_int32 found;
284 char *selnameh, *selnamev;
285 SELA *sela;
286 PIX *pixt1, *pixt2, *pixt3;
287
288 if (!pixs)
289 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
290 if (pixGetDepth(pixs) != 1)
291 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
292 if (hsize < 1 || vsize < 1)
293 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
294
295 if (hsize == 1 && vsize == 1)
296 return pixCopy(pixd, pixs);
297
298 sela = selaAddBasic(NULL);
299 found = TRUE;
300 selnameh = selnamev = NULL;
301 if (hsize > 1) {
302 selnameh = selaGetBrickName(sela, hsize, 1);
303 if (!selnameh) found = FALSE;
304 }
305 if (vsize > 1) {
306 selnamev = selaGetBrickName(sela, 1, vsize);
307 if (!selnamev) found = FALSE;
308 }
309 selaDestroy(&sela);
310 if (!found) {
311 L_INFO("Calling the decomposable dwa function\n", __func__);
312 if (selnameh) LEPT_FREE(selnameh);
313 if (selnamev) LEPT_FREE(selnamev);
314 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
315 }
316
317 if (vsize == 1) {
318 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnameh);
319 LEPT_FREE(selnameh);
320 } else if (hsize == 1) {
321 pixt2 = pixMorphDwa_1(NULL, pixs, L_MORPH_ERODE, selnamev);
322 LEPT_FREE(selnamev);
323 } else {
324 pixt1 = pixAddBorder(pixs, 32, 0);
325 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
326 pixFMorphopGen_1(pixt1, pixt3, L_MORPH_ERODE, selnamev);
327 pixt2 = pixRemoveBorder(pixt1, 32);
328 pixDestroy(&pixt1);
329 pixDestroy(&pixt3);
330 LEPT_FREE(selnameh);
331 LEPT_FREE(selnamev);
332 }
333
334 if (!pixd)
335 return pixt2;
336
337 pixTransferAllData(pixd, &pixt2, 0, 0);
338 return pixd;
339 }
340
341
342 /*!
343 * \brief pixOpenBrickDwa()
344 *
345 * \param[in] pixd [optional]; this can be null, equal to pixs,
346 * or different from pixs
347 * \param[in] pixs 1 bpp
348 * \param[in] hsize width of brick Sel
349 * \param[in] vsize height of brick Sel
350 * \return pixd
351 *
352 * <pre>
353 * Notes:
354 * (1) These implement 2D brick Sels, using linear Sels generated
355 * with selaAddBasic().
356 * (2) A brick Sel has hits for all elements.
357 * (3) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
358 * (4) Do separably if both hsize and vsize are > 1.
359 * (5) It is necessary that both horizontal and vertical Sels
360 * of the input size are defined in the basic sela.
361 * (6) Note that we must always set or clear the border pixels
362 * before each operation, depending on the the b.c.
363 * (symmetric or asymmetric).
364 * (7) There are three cases:
365 * (a) pixd == null (result into new pixd)
366 * (b) pixd == pixs (in-place; writes result back to pixs)
367 * (c) pixd != pixs (puts result into existing pixd)
368 * (8) For clarity, if the case is known, use these patterns:
369 * (a) pixd = pixOpenBrickDwa(NULL, pixs, ...);
370 * (b) pixOpenBrickDwa(pixs, pixs, ...);
371 * (c) pixOpenBrickDwa(pixd, pixs, ...);
372 * (9) The size of the result is determined by pixs.
373 * (10) If either linear Sel is not found, this calls
374 * the appropriate decomposible function.
375 * </pre>
376 */
377 PIX *
378 pixOpenBrickDwa(PIX *pixd,
379 PIX *pixs,
380 l_int32 hsize,
381 l_int32 vsize)
382 {
383 l_int32 found;
384 char *selnameh, *selnamev;
385 SELA *sela;
386 PIX *pixt1, *pixt2, *pixt3;
387
388 if (!pixs)
389 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
390 if (pixGetDepth(pixs) != 1)
391 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
392 if (hsize < 1 || vsize < 1)
393 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
394
395 if (hsize == 1 && vsize == 1)
396 return pixCopy(pixd, pixs);
397
398 sela = selaAddBasic(NULL);
399 found = TRUE;
400 selnameh = selnamev = NULL;
401 if (hsize > 1) {
402 selnameh = selaGetBrickName(sela, hsize, 1);
403 if (!selnameh) found = FALSE;
404 }
405 if (vsize > 1) {
406 selnamev = selaGetBrickName(sela, 1, vsize);
407 if (!selnamev) found = FALSE;
408 }
409 selaDestroy(&sela);
410 if (!found) {
411 L_INFO("Calling the decomposable dwa function\n", __func__);
412 if (selnameh) LEPT_FREE(selnameh);
413 if (selnamev) LEPT_FREE(selnamev);
414 return pixOpenCompBrickDwa(pixd, pixs, hsize, vsize);
415 }
416
417 pixt1 = pixAddBorder(pixs, 32, 0);
418 if (vsize == 1) { /* horizontal only */
419 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnameh);
420 LEPT_FREE(selnameh);
421 } else if (hsize == 1) { /* vertical only */
422 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_OPEN, selnamev);
423 LEPT_FREE(selnamev);
424 } else { /* do separable */
425 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh);
426 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev);
427 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh);
428 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev);
429 LEPT_FREE(selnameh);
430 LEPT_FREE(selnamev);
431 pixDestroy(&pixt3);
432 }
433 pixt3 = pixRemoveBorder(pixt2, 32);
434 pixDestroy(&pixt1);
435 pixDestroy(&pixt2);
436
437 if (!pixd)
438 return pixt3;
439
440 pixTransferAllData(pixd, &pixt3, 0, 0);
441 return pixd;
442 }
443
444
445 /*!
446 * \brief pixCloseBrickDwa()
447 *
448 * \param[in] pixd [optional]; this can be null, equal to pixs,
449 * or different from pixs
450 * \param[in] pixs 1 bpp
451 * \param[in] hsize width of brick Sel
452 * \param[in] vsize height of brick Sel
453 * \return pixd
454 *
455 * <pre>
456 * Notes:
457 * (1) This is a 'safe' closing; we add an extra border of 32 OFF
458 * pixels for the standard asymmetric b.c.
459 * (2) These implement 2D brick Sels, using linear Sels generated
460 * with selaAddBasic().
461 * (3) A brick Sel has hits for all elements.
462 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
463 * (5) Do separably if both hsize and vsize are > 1.
464 * (6) It is necessary that both horizontal and vertical Sels
465 * of the input size are defined in the basic sela.
466 * (7) Note that we must always set or clear the border pixels
467 * before each operation, depending on the the b.c.
468 * (symmetric or asymmetric).
469 * (8) There are three cases:
470 * (a) pixd == null (result into new pixd)
471 * (b) pixd == pixs (in-place; writes result back to pixs)
472 * (c) pixd != pixs (puts result into existing pixd)
473 * (9) For clarity, if the case is known, use these patterns:
474 * (a) pixd = pixCloseBrickDwa(NULL, pixs, ...);
475 * (b) pixCloseBrickDwa(pixs, pixs, ...);
476 * (c) pixCloseBrickDwa(pixd, pixs, ...);
477 * (10) The size of the result is determined by pixs.
478 * (11) If either linear Sel is not found, this calls
479 * the appropriate decomposible function.
480 * </pre>
481 */
482 PIX *
483 pixCloseBrickDwa(PIX *pixd,
484 PIX *pixs,
485 l_int32 hsize,
486 l_int32 vsize)
487 {
488 l_int32 bordercolor, bordersize, found;
489 char *selnameh, *selnamev;
490 SELA *sela;
491 PIX *pixt1, *pixt2, *pixt3;
492
493 if (!pixs)
494 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
495 if (pixGetDepth(pixs) != 1)
496 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
497 if (hsize < 1 || vsize < 1)
498 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
499
500 if (hsize == 1 && vsize == 1)
501 return pixCopy(pixd, pixs);
502
503 sela = selaAddBasic(NULL);
504 found = TRUE;
505 selnameh = selnamev = NULL;
506 if (hsize > 1) {
507 selnameh = selaGetBrickName(sela, hsize, 1);
508 if (!selnameh) found = FALSE;
509 }
510 if (vsize > 1) {
511 selnamev = selaGetBrickName(sela, 1, vsize);
512 if (!selnamev) found = FALSE;
513 }
514 selaDestroy(&sela);
515 if (!found) {
516 L_INFO("Calling the decomposable dwa function\n", __func__);
517 if (selnameh) LEPT_FREE(selnameh);
518 if (selnamev) LEPT_FREE(selnamev);
519 return pixCloseCompBrickDwa(pixd, pixs, hsize, vsize);
520 }
521
522 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
523 * an extra 32 OFF pixels around the image (in addition to
524 * the 32 added pixels for all dwa operations), whereas with
525 * SYMMETRIC_MORPH_BC this is not necessary. */
526 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
527 if (bordercolor == 0) /* asymmetric b.c. */
528 bordersize = 64;
529 else /* symmetric b.c. */
530 bordersize = 32;
531 pixt1 = pixAddBorder(pixs, bordersize, 0);
532
533 if (vsize == 1) { /* horizontal only */
534 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh);
535 LEPT_FREE(selnameh);
536 } else if (hsize == 1) { /* vertical only */
537 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev);
538 LEPT_FREE(selnamev);
539 } else { /* do separable */
540 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh);
541 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev);
542 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh);
543 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev);
544 LEPT_FREE(selnameh);
545 LEPT_FREE(selnamev);
546 pixDestroy(&pixt3);
547 }
548 pixt3 = pixRemoveBorder(pixt2, bordersize);
549 pixDestroy(&pixt1);
550 pixDestroy(&pixt2);
551
552 if (!pixd)
553 return pixt3;
554
555 pixTransferAllData(pixd, &pixt3, 0, 0);
556 return pixd;
557 }
558
559
560 /*-----------------------------------------------------------------*
561 * Binary composite morphological (dwa) ops with brick Sels *
562 *-----------------------------------------------------------------*/
563 /*!
564 * \brief pixDilateCompBrickDwa()
565 *
566 * \param[in] pixd [optional]; this can be null, equal to pixs,
567 * or different from pixs
568 * \param[in] pixs 1 bpp
569 * \param[in] hsize width of brick Sel
570 * \param[in] vsize height of brick Sel
571 * \return pixd
572 *
573 * <pre>
574 * Notes:
575 * (1) These implement a separable composite dilation with 2D brick Sels.
576 * (2) For efficiency, it may decompose each linear morphological
577 * operation into two (brick + comb).
578 * (3) A brick Sel has hits for all elements.
579 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
580 * (5) Do separably if both hsize and vsize are > 1.
581 * (6) It is necessary that both horizontal and vertical Sels
582 * of the input size are defined in the basic sela.
583 * (7) There are three cases:
584 * (a) pixd == null (result into new pixd)
585 * (b) pixd == pixs (in-place; writes result back to pixs)
586 * (c) pixd != pixs (puts result into existing pixd)
587 * (8) For clarity, if the case is known, use these patterns:
588 * (a) pixd = pixDilateCompBrickDwa(NULL, pixs, ...);
589 * (b) pixDilateCompBrickDwa(pixs, pixs, ...);
590 * (c) pixDilateCompBrickDwa(pixd, pixs, ...);
591 * (9) The size of pixd is determined by pixs.
592 * (10) CAUTION: both hsize and vsize are being decomposed.
593 * The decomposer chooses a product of sizes (call them
594 * 'terms') for each that is close to the input size,
595 * but not necessarily equal to it. It attempts to optimize:
596 * (a) for consistency with the input values: the product
597 * of terms is close to the input size
598 * (b) for efficiency of the operation: the sum of the
599 * terms is small; ideally about twice the square
600 * root of the input size.
601 * So, for example, if the input hsize = 37, which is
602 * a prime number, the decomposer will break this into two
603 * terms, 6 and 6, so that the net result is a dilation
604 * with hsize = 36.
605 * </pre>
606 */
607 PIX *
608 pixDilateCompBrickDwa(PIX *pixd,
609 PIX *pixs,
610 l_int32 hsize,
611 l_int32 vsize)
612 {
613 char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
614 l_int32 hsize1, hsize2, vsize1, vsize2;
615 PIX *pixt1, *pixt2, *pixt3;
616
617 if (!pixs)
618 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
619 if (pixGetDepth(pixs) != 1)
620 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
621 if (hsize < 1 || vsize < 1)
622 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
623 if (hsize > 63 || vsize > 63)
624 return pixDilateCompBrickExtendDwa(pixd, pixs, hsize, vsize);
625
626 if (hsize == 1 && vsize == 1)
627 return pixCopy(pixd, pixs);
628
629 hsize1 = hsize2 = vsize1 = vsize2 = 1;
630 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
631 if (hsize > 1)
632 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
633 &selnameh2, NULL, NULL);
634 if (vsize > 1)
635 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
636 &selnamev1, &selnamev2);
637
638 #if DEBUG_SEL_LOOKUP
639 lept_stderr("nameh1=%s, nameh2=%s, namev1=%s, namev2=%s\n",
640 selnameh1, selnameh2, selnamev1, selnamev2);
641 lept_stderr("hsize1=%d, hsize2=%d, vsize1=%d, vsize2=%d\n",
642 hsize1, hsize2, vsize1, vsize2);
643 #endif /* DEBUG_SEL_LOOKUP */
644
645 pixt1 = pixAddBorder(pixs, 64, 0);
646 if (vsize == 1) {
647 if (hsize2 == 1) {
648 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
649 } else {
650 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
651 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
652 pixDestroy(&pixt3);
653 }
654 } else if (hsize == 1) {
655 if (vsize2 == 1) {
656 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
657 } else {
658 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
659 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
660 pixDestroy(&pixt3);
661 }
662 } else { /* vsize and hsize both > 1 */
663 if (hsize2 == 1) {
664 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
665 } else {
666 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
667 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_DILATE, selnameh2);
668 pixDestroy(&pixt2);
669 }
670 if (vsize2 == 1) {
671 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
672 } else {
673 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
674 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_DILATE, selnamev2);
675 }
676 pixDestroy(&pixt3);
677 }
678 pixDestroy(&pixt1);
679 pixt1 = pixRemoveBorder(pixt2, 64);
680 pixDestroy(&pixt2);
681 if (selnameh1) LEPT_FREE(selnameh1);
682 if (selnameh2) LEPT_FREE(selnameh2);
683 if (selnamev1) LEPT_FREE(selnamev1);
684 if (selnamev2) LEPT_FREE(selnamev2);
685
686 if (!pixd)
687 return pixt1;
688
689 pixTransferAllData(pixd, &pixt1, 0, 0);
690 return pixd;
691 }
692
693
694 /*!
695 * \brief pixErodeCompBrickDwa()
696 *
697 * \param[in] pixd [optional]; this can be null, equal to pixs,
698 * or different from pixs
699 * \param[in] pixs 1 bpp
700 * \param[in] hsize width of brick Sel
701 * \param[in] vsize height of brick Sel
702 * \return pixd
703 *
704 * <pre>
705 * Notes:
706 * (1) These implement a separable composite erosion with 2D brick Sels.
707 * (2) For efficiency, it may decompose each linear morphological
708 * operation into two (brick + comb).
709 * (3) A brick Sel has hits for all elements.
710 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
711 * (5) Do separably if both hsize and vsize are > 1.
712 * (6) It is necessary that both horizontal and vertical Sels
713 * of the input size are defined in the basic sela.
714 * (7) There are three cases:
715 * (a) pixd == null (result into new pixd)
716 * (b) pixd == pixs (in-place; writes result back to pixs)
717 * (c) pixd != pixs (puts result into existing pixd)
718 * (8) For clarity, if the case is known, use these patterns:
719 * (a) pixd = pixErodeCompBrickDwa(NULL, pixs, ...);
720 * (b) pixErodeCompBrickDwa(pixs, pixs, ...);
721 * (c) pixErodeCompBrickDwa(pixd, pixs, ...);
722 * (9) The size of pixd is determined by pixs.
723 * (10) CAUTION: both hsize and vsize are being decomposed.
724 * The decomposer chooses a product of sizes (call them
725 * 'terms') for each that is close to the input size,
726 * but not necessarily equal to it. It attempts to optimize:
727 * (a) for consistency with the input values: the product
728 * of terms is close to the input size
729 * (b) for efficiency of the operation: the sum of the
730 * terms is small; ideally about twice the square
731 * root of the input size.
732 * So, for example, if the input hsize = 37, which is
733 * a prime number, the decomposer will break this into two
734 * terms, 6 and 6, so that the net result is a dilation
735 * with hsize = 36.
736 * </pre>
737 */
738 PIX *
739 pixErodeCompBrickDwa(PIX *pixd,
740 PIX *pixs,
741 l_int32 hsize,
742 l_int32 vsize)
743 {
744 char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
745 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor;
746 PIX *pixt1, *pixt2, *pixt3;
747
748 if (!pixs)
749 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
750 if (pixGetDepth(pixs) != 1)
751 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
752 if (hsize < 1 || vsize < 1)
753 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
754 if (hsize > 63 || vsize > 63)
755 return pixErodeCompBrickExtendDwa(pixd, pixs, hsize, vsize);
756
757 if (hsize == 1 && vsize == 1)
758 return pixCopy(pixd, pixs);
759
760 hsize1 = hsize2 = vsize1 = vsize2 = 1;
761 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
762 if (hsize > 1)
763 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
764 &selnameh2, NULL, NULL);
765 if (vsize > 1)
766 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
767 &selnamev1, &selnamev2);
768
769 /* For symmetric b.c., bordercolor == 1 for erosion */
770 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
771 pixt1 = pixAddBorder(pixs, 64, bordercolor);
772
773 if (vsize == 1) {
774 if (hsize2 == 1) {
775 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
776 } else {
777 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
778 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
779 pixDestroy(&pixt3);
780 }
781 } else if (hsize == 1) {
782 if (vsize2 == 1) {
783 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
784 } else {
785 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
786 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
787 pixDestroy(&pixt3);
788 }
789 } else { /* vsize and hsize both > 1 */
790 if (hsize2 == 1) {
791 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
792 } else {
793 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
794 pixt3 = pixFMorphopGen_2(NULL, pixt2, L_MORPH_ERODE, selnameh2);
795 pixDestroy(&pixt2);
796 }
797 if (vsize2 == 1) {
798 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
799 } else {
800 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
801 pixFMorphopGen_2(pixt2, pixt2, L_MORPH_ERODE, selnamev2);
802 }
803 pixDestroy(&pixt3);
804 }
805 pixDestroy(&pixt1);
806 pixt1 = pixRemoveBorder(pixt2, 64);
807 pixDestroy(&pixt2);
808 if (selnameh1) LEPT_FREE(selnameh1);
809 if (selnameh2) LEPT_FREE(selnameh2);
810 if (selnamev1) LEPT_FREE(selnamev1);
811 if (selnamev2) LEPT_FREE(selnamev2);
812
813 if (!pixd)
814 return pixt1;
815
816 pixTransferAllData(pixd, &pixt1, 0, 0);
817 return pixd;
818 }
819
820
821 /*!
822 * \brief pixOpenCompBrickDwa()
823 *
824 * \param[in] pixd [optional]; this can be null, equal to pixs,
825 * or different from pixs
826 * \param[in] pixs 1 bpp
827 * \param[in] hsize width of brick Sel
828 * \param[in] vsize height of brick Sel
829 * \return pixd
830 *
831 * <pre>
832 * Notes:
833 * (1) These implement a separable composite opening with 2D brick Sels.
834 * (2) For efficiency, it may decompose each linear morphological
835 * operation into two (brick + comb).
836 * (3) A brick Sel has hits for all elements.
837 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
838 * (5) Do separably if both hsize and vsize are > 1.
839 * (6) It is necessary that both horizontal and vertical Sels
840 * of the input size are defined in the basic sela.
841 * (7) There are three cases:
842 * (a) pixd == null (result into new pixd)
843 * (b) pixd == pixs (in-place; writes result back to pixs)
844 * (c) pixd != pixs (puts result into existing pixd)
845 * (8) For clarity, if the case is known, use these patterns:
846 * (a) pixd = pixOpenCompBrickDwa(NULL, pixs, ...);
847 * (b) pixOpenCompBrickDwa(pixs, pixs, ...);
848 * (c) pixOpenCompBrickDwa(pixd, pixs, ...);
849 * (9) The size of pixd is determined by pixs.
850 * (10) CAUTION: both hsize and vsize are being decomposed.
851 * The decomposer chooses a product of sizes (call them
852 * 'terms') for each that is close to the input size,
853 * but not necessarily equal to it. It attempts to optimize:
854 * (a) for consistency with the input values: the product
855 * of terms is close to the input size
856 * (b) for efficiency of the operation: the sum of the
857 * terms is small; ideally about twice the square
858 * root of the input size.
859 * So, for example, if the input hsize = 37, which is
860 * a prime number, the decomposer will break this into two
861 * terms, 6 and 6, so that the net result is a dilation
862 * with hsize = 36.
863 * </pre>
864 */
865 PIX *
866 pixOpenCompBrickDwa(PIX *pixd,
867 PIX *pixs,
868 l_int32 hsize,
869 l_int32 vsize)
870 {
871 char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
872 l_int32 hsize1, hsize2, vsize1, vsize2, bordercolor;
873 PIX *pixt1, *pixt2, *pixt3;
874
875 if (!pixs)
876 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
877 if (pixGetDepth(pixs) != 1)
878 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
879 if (hsize < 1 || vsize < 1)
880 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
881 if (hsize > 63 || vsize > 63)
882 return pixOpenCompBrickExtendDwa(pixd, pixs, hsize, vsize);
883
884 if (hsize == 1 && vsize == 1)
885 return pixCopy(pixd, pixs);
886
887 hsize1 = hsize2 = vsize1 = vsize2 = 1;
888 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
889 if (hsize > 1)
890 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
891 &selnameh2, NULL, NULL);
892 if (vsize > 1)
893 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
894 &selnamev1, &selnamev2);
895
896 /* For symmetric b.c., initialize erosion with bordercolor == 1 */
897 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
898 pixt1 = pixAddBorder(pixs, 64, bordercolor);
899
900 if (vsize == 1) {
901 if (hsize2 == 1) {
902 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
903 if (bordercolor == 1)
904 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
905 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnameh1);
906 } else {
907 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
908 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
909 if (bordercolor == 1)
910 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
911 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
912 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
913 }
914 } else if (hsize == 1) {
915 if (vsize2 == 1) {
916 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
917 if (bordercolor == 1)
918 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
919 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
920 } else {
921 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnamev1);
922 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnamev2);
923 if (bordercolor == 1)
924 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
925 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
926 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
927 }
928 } else { /* vsize and hsize both > 1 */
929 if (hsize2 == 1 && vsize2 == 1) {
930 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
931 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
932 if (bordercolor == 1)
933 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
934 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
935 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
936 } else if (vsize2 == 1) {
937 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
938 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
939 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
940 if (bordercolor == 1)
941 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
942 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
943 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnameh2);
944 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnamev1);
945 } else if (hsize2 == 1) {
946 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
947 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_ERODE, selnamev1);
948 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnamev2);
949 if (bordercolor == 1)
950 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_CLR);
951 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_DILATE, selnameh1);
952 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
953 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
954 } else { /* both directions are combed */
955 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_ERODE, selnameh1);
956 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_ERODE, selnameh2);
957 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
958 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
959 if (bordercolor == 1)
960 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_CLR);
961 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnameh1);
962 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnameh2);
963 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
964 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
965 }
966 }
967 pixDestroy(&pixt3);
968
969 pixDestroy(&pixt1);
970 pixt1 = pixRemoveBorder(pixt2, 64);
971 pixDestroy(&pixt2);
972 if (selnameh1) LEPT_FREE(selnameh1);
973 if (selnameh2) LEPT_FREE(selnameh2);
974 if (selnamev1) LEPT_FREE(selnamev1);
975 if (selnamev2) LEPT_FREE(selnamev2);
976
977 if (!pixd)
978 return pixt1;
979
980 pixTransferAllData(pixd, &pixt1, 0, 0);
981 return pixd;
982 }
983
984
985 /*!
986 * \brief pixCloseCompBrickDwa()
987 *
988 * \param[in] pixd [optional]; this can be null, equal to pixs,
989 * or different from pixs
990 * \param[in] pixs 1 bpp
991 * \param[in] hsize width of brick Sel
992 * \param[in] vsize height of brick Sel
993 * \return pixd
994 *
995 * <pre>
996 * Notes:
997 * (1) This implements a separable composite safe closing with 2D
998 * brick Sels.
999 * (2) For efficiency, it may decompose each linear morphological
1000 * operation into two (brick + comb).
1001 * (3) A brick Sel has hits for all elements.
1002 * (4) The origin of the Sel is at (x, y) = (hsize/2, vsize/2)
1003 * (5) Do separably if both hsize and vsize are > 1.
1004 * (6) It is necessary that both horizontal and vertical Sels
1005 * of the input size are defined in the basic sela.
1006 * (7) There are three cases:
1007 * (a) pixd == null (result into new pixd)
1008 * (b) pixd == pixs (in-place; writes result back to pixs)
1009 * (c) pixd != pixs (puts result into existing pixd)
1010 * (8) For clarity, if the case is known, use these patterns:
1011 * (a) pixd = pixCloseCompBrickDwa(NULL, pixs, ...);
1012 * (b) pixCloseCompBrickDwa(pixs, pixs, ...);
1013 * (c) pixCloseCompBrickDwa(pixd, pixs, ...);
1014 * (9) The size of pixd is determined by pixs.
1015 * (10) CAUTION: both hsize and vsize are being decomposed.
1016 * The decomposer chooses a product of sizes (call them
1017 * 'terms') for each that is close to the input size,
1018 * but not necessarily equal to it. It attempts to optimize:
1019 * (a) for consistency with the input values: the product
1020 * of terms is close to the input size
1021 * (b) for efficiency of the operation: the sum of the
1022 * terms is small; ideally about twice the square
1023 * root of the input size.
1024 * So, for example, if the input hsize = 37, which is
1025 * a prime number, the decomposer will break this into two
1026 * terms, 6 and 6, so that the net result is a dilation
1027 * with hsize = 36.
1028 * </pre>
1029 */
1030 PIX *
1031 pixCloseCompBrickDwa(PIX *pixd,
1032 PIX *pixs,
1033 l_int32 hsize,
1034 l_int32 vsize)
1035 {
1036 char *selnameh1, *selnameh2, *selnamev1, *selnamev2;
1037 l_int32 hsize1, hsize2, vsize1, vsize2, setborder;
1038 PIX *pixt1, *pixt2, *pixt3;
1039
1040 if (!pixs)
1041 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1042 if (pixGetDepth(pixs) != 1)
1043 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1044 if (hsize < 1 || vsize < 1)
1045 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1046 if (hsize > 63 || vsize > 63)
1047 return pixCloseCompBrickExtendDwa(pixd, pixs, hsize, vsize);
1048
1049 if (hsize == 1 && vsize == 1)
1050 return pixCopy(pixd, pixs);
1051
1052 hsize1 = hsize2 = vsize1 = vsize2 = 1;
1053 selnameh1 = selnameh2 = selnamev1 = selnamev2 = NULL;
1054 if (hsize > 1)
1055 getCompositeParameters(hsize, &hsize1, &hsize2, &selnameh1,
1056 &selnameh2, NULL, NULL);
1057 if (vsize > 1)
1058 getCompositeParameters(vsize, &vsize1, &vsize2, NULL, NULL,
1059 &selnamev1, &selnamev2);
1060
1061 pixt3 = NULL;
1062 /* For symmetric b.c., PIX_SET border for erosions */
1063 setborder = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
1064 pixt1 = pixAddBorder(pixs, 64, 0);
1065
1066 if (vsize == 1) {
1067 if (hsize2 == 1) {
1068 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnameh1);
1069 } else {
1070 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1071 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1072 if (setborder == 1)
1073 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1074 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1075 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
1076 }
1077 } else if (hsize == 1) {
1078 if (vsize2 == 1) {
1079 pixt2 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_CLOSE, selnamev1);
1080 } else {
1081 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnamev1);
1082 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnamev2);
1083 if (setborder == 1)
1084 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1085 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1086 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1087 }
1088 } else { /* vsize and hsize both > 1 */
1089 if (hsize2 == 1 && vsize2 == 1) {
1090 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1091 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
1092 if (setborder == 1)
1093 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1094 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1095 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
1096 } else if (vsize2 == 1) {
1097 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1098 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1099 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
1100 if (setborder == 1)
1101 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
1102 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
1103 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_ERODE, selnameh2);
1104 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnamev1);
1105 } else if (hsize2 == 1) {
1106 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1107 pixt2 = pixFMorphopGen_1(NULL, pixt3, L_MORPH_DILATE, selnamev1);
1108 pixFMorphopGen_2(pixt3, pixt2, L_MORPH_DILATE, selnamev2);
1109 if (setborder == 1)
1110 pixSetOrClearBorder(pixt3, 64, 64, 64, 64, PIX_SET);
1111 pixFMorphopGen_1(pixt2, pixt3, L_MORPH_ERODE, selnameh1);
1112 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1113 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1114 } else { /* both directions are combed */
1115 pixt3 = pixFMorphopGen_1(NULL, pixt1, L_MORPH_DILATE, selnameh1);
1116 pixt2 = pixFMorphopGen_2(NULL, pixt3, L_MORPH_DILATE, selnameh2);
1117 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_DILATE, selnamev1);
1118 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_DILATE, selnamev2);
1119 if (setborder == 1)
1120 pixSetOrClearBorder(pixt2, 64, 64, 64, 64, PIX_SET);
1121 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnameh1);
1122 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnameh2);
1123 pixFMorphopGen_1(pixt3, pixt2, L_MORPH_ERODE, selnamev1);
1124 pixFMorphopGen_2(pixt2, pixt3, L_MORPH_ERODE, selnamev2);
1125 }
1126 }
1127 pixDestroy(&pixt3);
1128
1129 pixDestroy(&pixt1);
1130 pixt1 = pixRemoveBorder(pixt2, 64);
1131 pixDestroy(&pixt2);
1132 if (selnameh1) LEPT_FREE(selnameh1);
1133 if (selnameh2) LEPT_FREE(selnameh2);
1134 if (selnamev1) LEPT_FREE(selnamev1);
1135 if (selnamev2) LEPT_FREE(selnamev2);
1136
1137 if (!pixd)
1138 return pixt1;
1139
1140 pixTransferAllData(pixd, &pixt1, 0, 0);
1141 return pixd;
1142 }
1143
1144
1145 /*--------------------------------------------------------------------------*
1146 * Binary expanded composite morphological (dwa) ops with brick Sels *
1147 *--------------------------------------------------------------------------*/
1148 /*!
1149 * \brief pixDilateCompBrickExtendDwa()
1150 *
1151 * \param[in] pixd [optional]; this can be null, equal to pixs,
1152 * or different from pixs
1153 * \param[in] pixs 1 bpp
1154 * \param[in] hsize width of brick Sel
1155 * \param[in] vsize height of brick Sel
1156 * \return pixd
1157 *
1158 * <pre>
1159 * Notes:
1160 * (1) Ankur Jain suggested and implemented extending the composite
1161 * DWA operations beyond the 63 pixel limit. This is a
1162 * simplified and approximate implementation of the extension.
1163 * This allows arbitrary Dwa morph operations using brick Sels,
1164 * by decomposing the horizontal and vertical dilations into
1165 * a sequence of 63-element dilations plus a dilation of size
1166 * between 3 and 62.
1167 * (2) The 63-element dilations are exact, whereas the extra dilation
1168 * is approximate, because the underlying decomposition is
1169 * in pixDilateCompBrickDwa(). See there for further details.
1170 * (3) There are three cases:
1171 * (a) pixd == null (result into new pixd)
1172 * (b) pixd == pixs (in-place; writes result back to pixs)
1173 * (c) pixd != pixs (puts result into existing pixd)
1174 * (4) There is no need to call this directly: pixDilateCompBrickDwa()
1175 * calls this function if either brick dimension exceeds 63.
1176 * </pre>
1177 */
1178 PIX *
1179 pixDilateCompBrickExtendDwa(PIX *pixd,
1180 PIX *pixs,
1181 l_int32 hsize,
1182 l_int32 vsize)
1183 {
1184 l_int32 i, nops, nh, extrah, nv, extrav;
1185 PIX *pixt1, *pixt2, *pixt3;
1186
1187 if (!pixs)
1188 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1189 if (pixGetDepth(pixs) != 1)
1190 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1191 if (hsize < 1 || vsize < 1)
1192 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1193
1194 if (hsize < 64 && vsize < 64)
1195 return pixDilateCompBrickDwa(pixd, pixs, hsize, vsize);
1196
1197 if (hsize > 63)
1198 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
1199 if (vsize > 63)
1200 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
1201
1202 /* Horizontal dilation first: pixs --> pixt2. Do not alter pixs. */
1203 pixt1 = pixCreateTemplate(pixs); /* temp image */
1204 if (hsize == 1) {
1205 pixt2 = pixClone(pixs);
1206 } else if (hsize < 64) {
1207 pixt2 = pixDilateCompBrickDwa(NULL, pixs, hsize, 1);
1208 } else if (hsize == 64) { /* approximate */
1209 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
1210 } else {
1211 nops = (extrah < 3) ? nh : nh + 1;
1212 if (nops & 1) { /* odd */
1213 if (extrah > 2)
1214 pixt2 = pixDilateCompBrickDwa(NULL, pixs, extrah, 1);
1215 else
1216 pixt2 = pixDilateCompBrickDwa(NULL, pixs, 63, 1);
1217 for (i = 0; i < nops / 2; i++) {
1218 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
1219 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
1220 }
1221 } else { /* nops even */
1222 if (extrah > 2) {
1223 pixDilateCompBrickDwa(pixt1, pixs, extrah, 1);
1224 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
1225 } else { /* they're all 63s */
1226 pixDilateCompBrickDwa(pixt1, pixs, 63, 1);
1227 pixt2 = pixDilateCompBrickDwa(NULL, pixt1, 63, 1);
1228 }
1229 for (i = 0; i < nops / 2 - 1; i++) {
1230 pixDilateCompBrickDwa(pixt1, pixt2, 63, 1);
1231 pixDilateCompBrickDwa(pixt2, pixt1, 63, 1);
1232 }
1233 }
1234 }
1235
1236 /* Vertical dilation: pixt2 --> pixt3. */
1237 if (vsize == 1) {
1238 pixt3 = pixClone(pixt2);
1239 } else if (vsize < 64) {
1240 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, vsize);
1241 } else if (vsize == 64) { /* approximate */
1242 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
1243 } else {
1244 nops = (extrav < 3) ? nv : nv + 1;
1245 if (nops & 1) { /* odd */
1246 if (extrav > 2)
1247 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, extrav);
1248 else
1249 pixt3 = pixDilateCompBrickDwa(NULL, pixt2, 1, 63);
1250 for (i = 0; i < nops / 2; i++) {
1251 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
1252 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
1253 }
1254 } else { /* nops even */
1255 if (extrav > 2) {
1256 pixDilateCompBrickDwa(pixt1, pixt2, 1, extrav);
1257 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
1258 } else { /* they're all 63s */
1259 pixDilateCompBrickDwa(pixt1, pixt2, 1, 63);
1260 pixt3 = pixDilateCompBrickDwa(NULL, pixt1, 1, 63);
1261 }
1262 for (i = 0; i < nops / 2 - 1; i++) {
1263 pixDilateCompBrickDwa(pixt1, pixt3, 1, 63);
1264 pixDilateCompBrickDwa(pixt3, pixt1, 1, 63);
1265 }
1266 }
1267 }
1268 pixDestroy(&pixt1);
1269 pixDestroy(&pixt2);
1270
1271 if (!pixd)
1272 return pixt3;
1273
1274 pixTransferAllData(pixd, &pixt3, 0, 0);
1275 return pixd;
1276 }
1277
1278
1279 /*!
1280 * \brief pixErodeCompBrickExtendDwa()
1281 *
1282 * \param[in] pixd [optional]; this can be null, equal to pixs,
1283 * or different from pixs
1284 * \param[in] pixs 1 bpp
1285 * \param[in] hsize width of brick Sel
1286 * \param[in] vsize height of brick Sel
1287 * \return pixd
1288 *
1289 * <pre>
1290 * Notes:
1291 * (1) See pixDilateCompBrickExtendDwa() for usage.
1292 * (2) There is no need to call this directly: pixErodeCompBrickDwa()
1293 * calls this function if either brick dimension exceeds 63.
1294 * </pre>
1295 */
1296 PIX *
1297 pixErodeCompBrickExtendDwa(PIX *pixd,
1298 PIX *pixs,
1299 l_int32 hsize,
1300 l_int32 vsize)
1301 {
1302 l_int32 i, nops, nh, extrah, nv, extrav;
1303 PIX *pixt1, *pixt2, *pixt3;
1304
1305 if (!pixs)
1306 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1307 if (pixGetDepth(pixs) != 1)
1308 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1309 if (hsize < 1 || vsize < 1)
1310 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1311
1312 if (hsize < 64 && vsize < 64)
1313 return pixErodeCompBrickDwa(pixd, pixs, hsize, vsize);
1314
1315 if (hsize > 63)
1316 getExtendedCompositeParameters(hsize, &nh, &extrah, NULL);
1317 if (vsize > 63)
1318 getExtendedCompositeParameters(vsize, &nv, &extrav, NULL);
1319
1320 /* Horizontal erosion first: pixs --> pixt2. Do not alter pixs. */
1321 pixt1 = pixCreateTemplate(pixs); /* temp image */
1322 if (hsize == 1) {
1323 pixt2 = pixClone(pixs);
1324 } else if (hsize < 64) {
1325 pixt2 = pixErodeCompBrickDwa(NULL, pixs, hsize, 1);
1326 } else if (hsize == 64) { /* approximate */
1327 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
1328 } else {
1329 nops = (extrah < 3) ? nh : nh + 1;
1330 if (nops & 1) { /* odd */
1331 if (extrah > 2)
1332 pixt2 = pixErodeCompBrickDwa(NULL, pixs, extrah, 1);
1333 else
1334 pixt2 = pixErodeCompBrickDwa(NULL, pixs, 63, 1);
1335 for (i = 0; i < nops / 2; i++) {
1336 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
1337 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
1338 }
1339 } else { /* nops even */
1340 if (extrah > 2) {
1341 pixErodeCompBrickDwa(pixt1, pixs, extrah, 1);
1342 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
1343 } else { /* they're all 63s */
1344 pixErodeCompBrickDwa(pixt1, pixs, 63, 1);
1345 pixt2 = pixErodeCompBrickDwa(NULL, pixt1, 63, 1);
1346 }
1347 for (i = 0; i < nops / 2 - 1; i++) {
1348 pixErodeCompBrickDwa(pixt1, pixt2, 63, 1);
1349 pixErodeCompBrickDwa(pixt2, pixt1, 63, 1);
1350 }
1351 }
1352 }
1353
1354 /* Vertical erosion: pixt2 --> pixt3. */
1355 if (vsize == 1) {
1356 pixt3 = pixClone(pixt2);
1357 } else if (vsize < 64) {
1358 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, vsize);
1359 } else if (vsize == 64) { /* approximate */
1360 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
1361 } else {
1362 nops = (extrav < 3) ? nv : nv + 1;
1363 if (nops & 1) { /* odd */
1364 if (extrav > 2)
1365 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, extrav);
1366 else
1367 pixt3 = pixErodeCompBrickDwa(NULL, pixt2, 1, 63);
1368 for (i = 0; i < nops / 2; i++) {
1369 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
1370 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
1371 }
1372 } else { /* nops even */
1373 if (extrav > 2) {
1374 pixErodeCompBrickDwa(pixt1, pixt2, 1, extrav);
1375 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
1376 } else { /* they're all 63s */
1377 pixErodeCompBrickDwa(pixt1, pixt2, 1, 63);
1378 pixt3 = pixErodeCompBrickDwa(NULL, pixt1, 1, 63);
1379 }
1380 for (i = 0; i < nops / 2 - 1; i++) {
1381 pixErodeCompBrickDwa(pixt1, pixt3, 1, 63);
1382 pixErodeCompBrickDwa(pixt3, pixt1, 1, 63);
1383 }
1384 }
1385 }
1386 pixDestroy(&pixt1);
1387 pixDestroy(&pixt2);
1388
1389 if (!pixd)
1390 return pixt3;
1391
1392 pixTransferAllData(pixd, &pixt3, 0, 0);
1393 return pixd;
1394 }
1395
1396
1397 /*!
1398 * \brief pixOpenCompBrickExtendDwa()
1399 *
1400 * \param[in] pixd [optional]; this can be null, equal to pixs,
1401 * or different from pixs
1402 * \param[in] pixs 1 bpp
1403 * \param[in] hsize width of brick Sel
1404 * \param[in] vsize height of brick Sel
1405 * \return pixd
1406 *
1407 * <pre>
1408 * Notes:
1409 * 1) There are three cases:
1410 * a) pixd == null (result into new pixd
1411 * b) pixd == pixs (in-place; writes result back to pixs
1412 * c) pixd != pixs (puts result into existing pixd
1413 * 2) There is no need to call this directly: pixOpenCompBrickDwa(
1414 * calls this function if either brick dimension exceeds 63.
1415 * </pre>
1416 */
1417 PIX *
1418 pixOpenCompBrickExtendDwa(PIX *pixd,
1419 PIX *pixs,
1420 l_int32 hsize,
1421 l_int32 vsize)
1422 {
1423 PIX *pixt;
1424
1425 if (!pixs)
1426 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1427 if (pixGetDepth(pixs) != 1)
1428 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1429 if (hsize < 1 || vsize < 1)
1430 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1431
1432 pixt = pixErodeCompBrickExtendDwa(NULL, pixs, hsize, vsize);
1433 pixd = pixDilateCompBrickExtendDwa(pixd, pixt, hsize, vsize);
1434 pixDestroy(&pixt);
1435 return pixd;
1436 }
1437
1438
1439 /*!
1440 * \brief pixCloseCompBrickExtendDwa()
1441 *
1442 * \param[in] pixd [optional]; this can be null, equal to pixs,
1443 * or different from pixs
1444 * \param[in] pixs 1 bpp
1445 * \param[in] hsize width of brick Sel
1446 * \param[in] vsize height of brick Sel
1447 * \return pixd
1448 *
1449 * <pre>
1450 * Notes:
1451 * 1) There are three cases:
1452 * a) pixd == null (result into new pixd
1453 * b) pixd == pixs (in-place; writes result back to pixs
1454 * c) pixd != pixs (puts result into existing pixd
1455 * 2) There is no need to call this directly: pixCloseCompBrickDwa(
1456 * calls this function if either brick dimension exceeds 63.
1457 * </pre>
1458 */
1459 PIX *
1460 pixCloseCompBrickExtendDwa(PIX *pixd,
1461 PIX *pixs,
1462 l_int32 hsize,
1463 l_int32 vsize)
1464 {
1465 l_int32 bordercolor, borderx, bordery;
1466 PIX *pixt1, *pixt2, *pixt3;
1467
1468 if (!pixs)
1469 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1470 if (pixGetDepth(pixs) != 1)
1471 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1472 if (hsize < 1 || vsize < 1)
1473 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1474
1475 /* For "safe closing" with ASYMMETRIC_MORPH_BC, we always need
1476 * an extra 32 OFF pixels around the image (in addition to
1477 * the 32 added pixels for all dwa operations), whereas with
1478 * SYMMETRIC_MORPH_BC this is not necessary. */
1479 bordercolor = getMorphBorderPixelColor(L_MORPH_ERODE, 1);
1480 if (bordercolor == 0) { /* asymmetric b.c. */
1481 borderx = 32 + (hsize / 64) * 32;
1482 bordery = 32 + (vsize / 64) * 32;
1483 } else { /* symmetric b.c. */
1484 borderx = bordery = 32;
1485 }
1486 pixt1 = pixAddBorderGeneral(pixs, borderx, borderx, bordery, bordery, 0);
1487
1488 pixt2 = pixDilateCompBrickExtendDwa(NULL, pixt1, hsize, vsize);
1489 pixErodeCompBrickExtendDwa(pixt1, pixt2, hsize, vsize);
1490
1491 pixt3 = pixRemoveBorderGeneral(pixt1, borderx, borderx, bordery, bordery);
1492 pixDestroy(&pixt1);
1493 pixDestroy(&pixt2);
1494
1495 if (!pixd)
1496 return pixt3;
1497
1498 pixTransferAllData(pixd, &pixt3, 0, 0);
1499 return pixd;
1500 }
1501
1502
1503 /*!
1504 * \brief getExtendedCompositeParameters()
1505 *
1506 * \param[in] size of linear Sel
1507 * \param[out] pn number of 63 wide convolutions
1508 * \param[out] pextra size of extra Sel
1509 * \param[out] pactualsize [optional] actual size used in operation
1510 * \return 0 if OK, 1 on error
1511 *
1512 * <pre>
1513 * Notes:
1514 * (1) The DWA implementation allows Sels to be used with hits
1515 * up to 31 pixels from the origin, either horizontally or
1516 * vertically. Larger Sels can be used if decomposed into
1517 * a set of operations with Sels not exceeding 63 pixels
1518 * in either width or height (and with the origin as close
1519 * to the center of the Sel as possible).
1520 * (2) This returns the decomposition of a linear Sel of length
1521 * %size into a set of %n Sels of length 63 plus an extra
1522 * Sel of length %extra.
1523 * (3) For notation, let w == %size, n == %n, and e == %extra.
1524 * We have 1 < e < 63.
1525 *
1526 * Then if w < 64, we have n = 0 and e = w.
1527 * The general formula for w > 63 is:
1528 * w = 63 + (n - 1) * 62 + (e - 1)
1529 *
1530 * Where did this come from? Each successive convolution with
1531 * a Sel of length L adds a total length (L - 1) to w.
1532 * This accounts for using 62 for each additional Sel of size 63,
1533 * and using (e - 1) for the additional Sel of size e.
1534 *
1535 * Solving for n and e for w > 63:
1536 * n = 1 + Int((w - 63) / 62)
1537 * e = w - 63 - (n - 1) * 62 + 1
1538 *
1539 * The extra part is decomposed into two factors f1 and f2,
1540 * and the actual size of the extra part is
1541 * e' = f1 * f2
1542 * Then the actual width is:
1543 * w' = 63 + (n - 1) * 62 + f1 * f2 - 1
1544 * </pre>
1545 */
1546 l_ok
1547 getExtendedCompositeParameters(l_int32 size,
1548 l_int32 *pn,
1549 l_int32 *pextra,
1550 l_int32 *pactualsize)
1551 {
1552 l_int32 n, extra, fact1, fact2;
1553
1554 if (!pn || !pextra)
1555 return ERROR_INT("&n and &extra not both defined", __func__, 1);
1556
1557 if (size <= 63) {
1558 n = 0;
1559 extra = L_MIN(1, size);
1560 } else { /* size > 63 */
1561 n = 1 + (l_int32)((size - 63) / 62);
1562 extra = size - 63 - (n - 1) * 62 + 1;
1563 }
1564
1565 if (pactualsize) {
1566 selectComposableSizes(extra, &fact1, &fact2);
1567 *pactualsize = 63 + (n - 1) * 62 + fact1 * fact2 - 1;
1568 }
1569
1570 *pn = n;
1571 *pextra = extra;
1572 return 0;
1573 }