comparison mupdf-source/thirdparty/leptonica/src/morph.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 morph.c
29 * <pre>
30 *
31 * Generic binary morphological ops implemented with rasterop
32 * PIX *pixDilate()
33 * PIX *pixErode()
34 * PIX *pixHMT()
35 * PIX *pixOpen()
36 * PIX *pixClose()
37 * PIX *pixCloseSafe()
38 * PIX *pixOpenGeneralized()
39 * PIX *pixCloseGeneralized()
40 *
41 * Binary morphological (raster) ops with brick Sels
42 * PIX *pixDilateBrick()
43 * PIX *pixErodeBrick()
44 * PIX *pixOpenBrick()
45 * PIX *pixCloseBrick()
46 * PIX *pixCloseSafeBrick()
47 *
48 * Binary composed morphological (raster) ops with brick Sels
49 * l_int32 selectComposableSels()
50 * l_int32 selectComposableSizes()
51 * PIX *pixDilateCompBrick()
52 * PIX *pixErodeCompBrick()
53 * PIX *pixOpenCompBrick()
54 * PIX *pixCloseCompBrick()
55 * PIX *pixCloseSafeCompBrick()
56 *
57 * Functions associated with boundary conditions
58 * void resetMorphBoundaryCondition()
59 * l_int32 getMorphBorderPixelColor()
60 *
61 * Static helpers for arg processing
62 * static PIX *processMorphArgs1()
63 * static PIX *processMorphArgs2()
64 *
65 * You are provided with many simple ways to do binary morphology.
66 * In particular, if you are using brick Sels, there are six
67 * convenient methods, all specially tailored for separable operations
68 * on brick Sels. A "brick" Sel is a Sel that is a rectangle
69 * of solid SEL_HITs with the origin at or near the center.
70 * Note that a brick Sel can have one dimension of size 1.
71 * This is very common. All the brick Sel operations are
72 * separable, meaning the operation is done first in the horizontal
73 * direction and then in the vertical direction. If one of the
74 * dimensions is 1, this is a special case where the operation is
75 * only performed in the other direction.
76 *
77 * These six brick Sel methods are enumerated as follows:
78 *
79 * (1) Brick Sels: pix*Brick(), where * = {Dilate, Erode, Open, Close}.
80 * These are separable rasterop implementations. The Sels are
81 * automatically generated, used, and destroyed at the end.
82 * You can get the result as a new Pix, in-place back into the src Pix,
83 * or written to another existing Pix.
84 *
85 * (2) Brick Sels: pix*CompBrick(), where * = {Dilate, Erode, Open, Close}.
86 * These are separable, 2-way composite, rasterop implementations.
87 * The Sels are automatically generated, used, and destroyed at the end.
88 * You can get the result as a new Pix, in-place back into the src Pix,
89 * or written to another existing Pix. For large Sels, these are
90 * considerably faster than the corresponding pix*Brick() functions.
91 * N.B.: The size of the Sels that are actually used are typically
92 * close to, but not exactly equal to, the size input to the function.
93 *
94 * (3) Brick Sels: pix*BrickDwa(), where * = {Dilate, Erode, Open, Close}.
95 * These are separable dwa (destination word accumulation)
96 * implementations. They use auto-gen'd dwa code. You can get
97 * the result as a new Pix, in-place back into the src Pix,
98 * or written to another existing Pix. This is typically
99 * about 3x faster than the analogous rasterop pix*Brick()
100 * function, but it has the limitation that the Sel size must
101 * be less than 63. This is pre-set to work on a number
102 * of pre-generated Sels. If you want to use other Sels, the
103 * code can be auto-gen'd for them; see the instructions in morphdwa.c.
104 *
105 * (4) Same as (1), but you run it through pixMorphSequence(), with
106 * the sequence string either compiled in or generated using snprintf.
107 * All intermediate images and Sels are created, used and destroyed.
108 * You always get the result as a new Pix. For example, you can
109 * specify a separable 11 x 17 brick opening as "o11.17",
110 * or you can specify the horizontal and vertical operations
111 * explicitly as "o11.1 + o1.11". See morphseq.c for details.
112 *
113 * (5) Same as (2), but you run it through pixMorphCompSequence(), with
114 * the sequence string either compiled in or generated using snprintf.
115 * All intermediate images and Sels are created, used and destroyed.
116 * You always get the result as a new Pix. See morphseq.c for details.
117 *
118 * (6) Same as (3), but you run it through pixMorphSequenceDwa(), with
119 * the sequence string either compiled in or generated using snprintf.
120 * All intermediate images and Sels are created, used and destroyed.
121 * You always get the result as a new Pix. See morphseq.c for details.
122 *
123 * If you are using Sels that are not bricks, you have two choices:
124 * (a) simplest: use the basic rasterop implementations (pixDilate(), ...)
125 * (b) fastest: generate the destination word accumumlation (dwa)
126 * code for your Sels and compile it with the library.
127 *
128 * For an example, see flipdetect.c, which gives implementations
129 * using hit-miss Sels with both the rasterop and dwa versions.
130 * For the latter, the dwa code resides in fliphmtgen.c, and it
131 * was generated by prog/flipselgen.c. Both the rasterop and dwa
132 * implementations are tested by prog/fliptest.c.
133 *
134 * A global constant MORPH_BC is used to set the boundary conditions
135 * for rasterop-based binary morphology. MORPH_BC, in morph.c,
136 * is set by default to ASYMMETRIC_MORPH_BC for a non-symmetric
137 * convention for boundary pixels in dilation and erosion:
138 * All pixels outside the image are assumed to be OFF
139 * for both dilation and erosion.
140 * To use a symmetric definition, see comments in pixErode()
141 * and reset MORPH_BC to SYMMETRIC_MORPH_BC, using
142 * resetMorphBoundaryCondition().
143 *
144 * Boundary artifacts are possible in closing when the non-symmetric
145 * boundary conditions are used, because foreground pixels very close
146 * to the edge can be removed. This can be avoided by using either
147 * the symmetric boundary conditions or the function pixCloseSafe(),
148 * which adds a border before the operation and removes it afterwards.
149 *
150 * The hit-miss transform (HMT) is the bit-and of 2 erosions:
151 * (erosion of the src by the hits) & (erosion of the bit-inverted
152 * src by the misses)
153 *
154 * The 'generalized opening' is an HMT followed by a dilation that uses
155 * only the hits of the hit-miss Sel.
156 * The 'generalized closing' is a dilation (again, with the hits
157 * of a hit-miss Sel), followed by the HMT.
158 * Both of these 'generalized' functions are idempotent.
159 *
160 * These functions are extensively tested in prog/binmorph1_reg.c,
161 * prog/binmorph2_reg.c, and prog/binmorph3_reg.c.
162 * </pre>
163 */
164
165 #ifdef HAVE_CONFIG_H
166 #include <config_auto.h>
167 #endif /* HAVE_CONFIG_H */
168
169 #include <math.h>
170 #include "allheaders.h"
171
172 /* Global constant; initialized here; must be declared extern
173 * in other files to access it directly. However, in most
174 * cases that is not necessary, because it can be reset
175 * using resetMorphBoundaryCondition(). */
176 LEPT_DLL l_int32 MORPH_BC = ASYMMETRIC_MORPH_BC;
177
178 /* We accept this cost in extra rasterops for decomposing exactly. */
179 static const l_int32 ACCEPTABLE_COST = 5;
180
181 /* Static helpers for arg processing */
182 static PIX * processMorphArgs1(PIX *pixd, PIX *pixs, SEL *sel, PIX **ppixt);
183 static PIX * processMorphArgs2(PIX *pixd, PIX *pixs, SEL *sel);
184
185
186 /*-----------------------------------------------------------------*
187 * Generic binary morphological ops implemented with rasterop *
188 *-----------------------------------------------------------------*/
189 /*!
190 * \brief pixDilate()
191 *
192 * \param[in] pixd [optional]; this can be null, equal to pixs,
193 * or different from pixs
194 * \param[in] pixs 1 bpp
195 * \param[in] sel
196 * \return pixd
197 *
198 * <pre>
199 * Notes:
200 * (1) This dilates src using hits in Sel.
201 * (2) There are three cases:
202 * (a) pixd == null (result into new pixd)
203 * (b) pixd == pixs (in-place; writes result back to pixs)
204 * (c) pixd != pixs (puts result into existing pixd)
205 * (3) For clarity, if the case is known, use these patterns:
206 * (a) pixd = pixDilate(NULL, pixs, ...);
207 * (b) pixDilate(pixs, pixs, ...);
208 * (c) pixDilate(pixd, pixs, ...);
209 * (4) The size of the result is determined by pixs.
210 * </pre>
211 */
212 PIX *
213 pixDilate(PIX *pixd,
214 PIX *pixs,
215 SEL *sel)
216 {
217 l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
218 PIX *pixt;
219
220 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
221 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
222
223 pixGetDimensions(pixs, &w, &h, NULL);
224 selGetParameters(sel, &sy, &sx, &cy, &cx);
225 pixClearAll(pixd);
226 for (i = 0; i < sy; i++) {
227 for (j = 0; j < sx; j++) {
228 seldata = sel->data[i][j];
229 if (seldata == 1) { /* src | dst */
230 pixRasterop(pixd, j - cx, i - cy, w, h, PIX_SRC | PIX_DST,
231 pixt, 0, 0);
232 }
233 }
234 }
235
236 pixDestroy(&pixt);
237 return pixd;
238 }
239
240
241 /*!
242 * \brief pixErode()
243 *
244 * \param[in] pixd [optional]; this can be null, equal to pixs,
245 * or different from pixs
246 * \param[in] pixs 1 bpp
247 * \param[in] sel
248 * \return pixd
249 *
250 * <pre>
251 * Notes:
252 * (1) This erodes src using hits in Sel.
253 * (2) There are three cases:
254 * (a) pixd == null (result into new pixd)
255 * (b) pixd == pixs (in-place; writes result back to pixs)
256 * (c) pixd != pixs (puts result into existing pixd)
257 * (3) For clarity, if the case is known, use these patterns:
258 * (a) pixd = pixErode(NULL, pixs, ...);
259 * (b) pixErode(pixs, pixs, ...);
260 * (c) pixErode(pixd, pixs, ...);
261 * (4) The size of the result is determined by pixs.
262 * </pre>
263 */
264 PIX *
265 pixErode(PIX *pixd,
266 PIX *pixs,
267 SEL *sel)
268 {
269 l_int32 i, j, w, h, sx, sy, cx, cy, seldata;
270 l_int32 xp, yp, xn, yn;
271 PIX *pixt;
272
273 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
274 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
275
276 pixGetDimensions(pixs, &w, &h, NULL);
277 selGetParameters(sel, &sy, &sx, &cy, &cx);
278 pixSetAll(pixd);
279 for (i = 0; i < sy; i++) {
280 for (j = 0; j < sx; j++) {
281 seldata = sel->data[i][j];
282 if (seldata == 1) { /* src & dst */
283 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
284 pixt, 0, 0);
285 }
286 }
287 }
288
289 /* Clear near edges. We do this for the asymmetric boundary
290 * condition convention that implements erosion assuming all
291 * pixels surrounding the image are OFF. If you use a
292 * use a symmetric b.c. convention, where the erosion is
293 * implemented assuming pixels surrounding the image
294 * are ON, these operations are omitted. */
295 if (MORPH_BC == ASYMMETRIC_MORPH_BC) {
296 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
297 if (xp > 0)
298 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
299 if (xn > 0)
300 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
301 if (yp > 0)
302 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
303 if (yn > 0)
304 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
305 }
306
307 pixDestroy(&pixt);
308 return pixd;
309 }
310
311
312 /*!
313 * \brief pixHMT()
314 *
315 * \param[in] pixd [optional]; this can be null, equal to pixs,
316 * or different from pixs
317 * \param[in] pixs 1 bpp
318 * \param[in] sel
319 * \return pixd
320 *
321 * <pre>
322 * Notes:
323 * (1) The hit-miss transform erodes the src, using both hits
324 * and misses in the Sel. It ANDs the shifted src for hits
325 * and ANDs the inverted shifted src for misses.
326 * (2) There are three cases:
327 * (a) pixd == null (result into new pixd)
328 * (b) pixd == pixs (in-place; writes result back to pixs)
329 * (c) pixd != pixs (puts result into existing pixd)
330 * (3) For clarity, if the case is known, use these patterns:
331 * (a) pixd = pixHMT(NULL, pixs, ...);
332 * (b) pixHMT(pixs, pixs, ...);
333 * (c) pixHMT(pixd, pixs, ...);
334 * (4) The size of the result is determined by pixs.
335 * </pre>
336 */
337 PIX *
338 pixHMT(PIX *pixd,
339 PIX *pixs,
340 SEL *sel)
341 {
342 l_int32 i, j, w, h, sx, sy, cx, cy, firstrasterop, seldata;
343 l_int32 xp, yp, xn, yn;
344 PIX *pixt;
345
346 if ((pixd = processMorphArgs1(pixd, pixs, sel, &pixt)) == NULL)
347 return (PIX *)ERROR_PTR("processMorphArgs1 failed", __func__, pixd);
348
349 pixGetDimensions(pixs, &w, &h, NULL);
350 selGetParameters(sel, &sy, &sx, &cy, &cx);
351 firstrasterop = TRUE;
352 for (i = 0; i < sy; i++) {
353 for (j = 0; j < sx; j++) {
354 seldata = sel->data[i][j];
355 if (seldata == 1) { /* hit */
356 if (firstrasterop == TRUE) { /* src only */
357 pixClearAll(pixd);
358 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC,
359 pixt, 0, 0);
360 firstrasterop = FALSE;
361 } else { /* src & dst */
362 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_SRC & PIX_DST,
363 pixt, 0, 0);
364 }
365 } else if (seldata == 2) { /* miss */
366 if (firstrasterop == TRUE) { /* ~src only */
367 pixSetAll(pixd);
368 pixRasterop(pixd, cx - j, cy - i, w, h, PIX_NOT(PIX_SRC),
369 pixt, 0, 0);
370 firstrasterop = FALSE;
371 } else { /* ~src & dst */
372 pixRasterop(pixd, cx - j, cy - i, w, h,
373 PIX_NOT(PIX_SRC) & PIX_DST,
374 pixt, 0, 0);
375 }
376 }
377 }
378 }
379
380 /* Clear near edges */
381 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
382 if (xp > 0)
383 pixRasterop(pixd, 0, 0, xp, h, PIX_CLR, NULL, 0, 0);
384 if (xn > 0)
385 pixRasterop(pixd, w - xn, 0, xn, h, PIX_CLR, NULL, 0, 0);
386 if (yp > 0)
387 pixRasterop(pixd, 0, 0, w, yp, PIX_CLR, NULL, 0, 0);
388 if (yn > 0)
389 pixRasterop(pixd, 0, h - yn, w, yn, PIX_CLR, NULL, 0, 0);
390
391 pixDestroy(&pixt);
392 return pixd;
393 }
394
395
396 /*!
397 * \brief pixOpen()
398 *
399 * \param[in] pixd [optional]; this can be null, equal to pixs,
400 * or different from pixs
401 * \param[in] pixs 1 bpp
402 * \param[in] sel
403 * \return pixd
404 *
405 * <pre>
406 * Notes:
407 * (1) Generic morphological opening, using hits in the Sel.
408 * (2) There are three cases:
409 * (a) pixd == null (result into new pixd)
410 * (b) pixd == pixs (in-place; writes result back to pixs)
411 * (c) pixd != pixs (puts result into existing pixd)
412 * (3) For clarity, if the case is known, use these patterns:
413 * (a) pixd = pixOpen(NULL, pixs, ...);
414 * (b) pixOpen(pixs, pixs, ...);
415 * (c) pixOpen(pixd, pixs, ...);
416 * (4) The size of the result is determined by pixs.
417 * </pre>
418 */
419 PIX *
420 pixOpen(PIX *pixd,
421 PIX *pixs,
422 SEL *sel)
423 {
424 PIX *pixt;
425
426 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
427 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
428
429 if ((pixt = pixErode(NULL, pixs, sel)) == NULL)
430 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
431 pixDilate(pixd, pixt, sel);
432 pixDestroy(&pixt);
433
434 return pixd;
435 }
436
437
438 /*!
439 * \brief pixClose()
440 *
441 * \param[in] pixd [optional]; this can be null, equal to pixs,
442 * or different from pixs
443 * \param[in] pixs 1 bpp
444 * \param[in] sel
445 * \return pixd
446 *
447 * <pre>
448 * Notes:
449 * (1) Generic morphological closing, using hits in the Sel.
450 * (2) This implementation is a strict dual of the opening if
451 * symmetric boundary conditions are used (see notes at top
452 * of this file).
453 * (3) There are three cases:
454 * (a) pixd == null (result into new pixd)
455 * (b) pixd == pixs (in-place; writes result back to pixs)
456 * (c) pixd != pixs (puts result into existing pixd)
457 * (4) For clarity, if the case is known, use these patterns:
458 * (a) pixd = pixClose(NULL, pixs, ...);
459 * (b) pixClose(pixs, pixs, ...);
460 * (c) pixClose(pixd, pixs, ...);
461 * (5) The size of the result is determined by pixs.
462 * </pre>
463 */
464 PIX *
465 pixClose(PIX *pixd,
466 PIX *pixs,
467 SEL *sel)
468 {
469 PIX *pixt;
470
471 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
472 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
473
474 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
475 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
476 pixErode(pixd, pixt, sel);
477 pixDestroy(&pixt);
478
479 return pixd;
480 }
481
482
483 /*!
484 * \brief pixCloseSafe()
485 *
486 * \param[in] pixd [optional]; this can be null, equal to pixs,
487 * or different from pixs
488 * \param[in] pixs 1 bpp
489 * \param[in] sel
490 * \return pixd
491 *
492 * <pre>
493 * Notes:
494 * (1) Generic morphological closing, using hits in the Sel.
495 * (2) If non-symmetric boundary conditions are used, this
496 * function adds a border of OFF pixels that is of
497 * sufficient size to avoid losing pixels from the dilation,
498 * and it removes the border after the operation is finished.
499 * It thus enforces a correct extensive result for closing.
500 * (3) If symmetric b.c. are used, it is not necessary to add
501 * and remove this border.
502 * (4) There are three cases:
503 * (a) pixd == null (result into new pixd)
504 * (b) pixd == pixs (in-place; writes result back to pixs)
505 * (c) pixd != pixs (puts result into existing pixd)
506 * (5) For clarity, if the case is known, use these patterns:
507 * (a) pixd = pixCloseSafe(NULL, pixs, ...);
508 * (b) pixCloseSafe(pixs, pixs, ...);
509 * (c) pixCloseSafe(pixd, pixs, ...);
510 * (6) The size of the result is determined by pixs.
511 * </pre>
512 */
513 PIX *
514 pixCloseSafe(PIX *pixd,
515 PIX *pixs,
516 SEL *sel)
517 {
518 l_int32 xp, yp, xn, yn, xmax, xbord;
519 PIX *pixt1, *pixt2;
520
521 if (!pixs)
522 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
523 if (!sel)
524 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
525 if (pixGetDepth(pixs) != 1)
526 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
527
528 /* Symmetric b.c. handles correctly without added pixels */
529 if (MORPH_BC == SYMMETRIC_MORPH_BC)
530 return pixClose(pixd, pixs, sel);
531
532 selFindMaxTranslations(sel, &xp, &yp, &xn, &yn);
533 xmax = L_MAX(xp, xn);
534 xbord = 32 * ((xmax + 31) / 32); /* full 32 bit words */
535
536 if ((pixt1 = pixAddBorderGeneral(pixs, xbord, xbord, yp, yn, 0)) == NULL)
537 return (PIX *)ERROR_PTR("pixt1 not made", __func__, pixd);
538 pixClose(pixt1, pixt1, sel);
539 if ((pixt2 = pixRemoveBorderGeneral(pixt1, xbord, xbord, yp, yn)) == NULL)
540 return (PIX *)ERROR_PTR("pixt2 not made", __func__, pixd);
541 pixDestroy(&pixt1);
542
543 if (!pixd)
544 return pixt2;
545
546 pixCopy(pixd, pixt2);
547 pixDestroy(&pixt2);
548 return pixd;
549 }
550
551
552 /*!
553 * \brief pixOpenGeneralized()
554 *
555 * \param[in] pixd [optional]; this can be null, equal to pixs,
556 * or different from pixs
557 * \param[in] pixs 1 bpp
558 * \param[in] sel
559 * \return pixd
560 *
561 * <pre>
562 * Notes:
563 * (1) Generalized morphological opening, using both hits and
564 * misses in the Sel.
565 * (2) This does a hit-miss transform, followed by a dilation
566 * using the hits.
567 * (3) There are three cases:
568 * (a) pixd == null (result into new pixd)
569 * (b) pixd == pixs (in-place; writes result back to pixs)
570 * (c) pixd != pixs (puts result into existing pixd)
571 * (4) For clarity, if the case is known, use these patterns:
572 * (a) pixd = pixOpenGeneralized(NULL, pixs, ...);
573 * (b) pixOpenGeneralized(pixs, pixs, ...);
574 * (c) pixOpenGeneralized(pixd, pixs, ...);
575 * (5) The size of the result is determined by pixs.
576 * </pre>
577 */
578 PIX *
579 pixOpenGeneralized(PIX *pixd,
580 PIX *pixs,
581 SEL *sel)
582 {
583 PIX *pixt;
584
585 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
586 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
587
588 if ((pixt = pixHMT(NULL, pixs, sel)) == NULL)
589 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
590 pixDilate(pixd, pixt, sel);
591 pixDestroy(&pixt);
592 return pixd;
593 }
594
595
596 /*!
597 * \brief pixCloseGeneralized()
598 *
599 * \param[in] pixd [optional]; this can be null, equal to pixs,
600 * or different from pixs
601 * \param[in] pixs 1 bpp
602 * \param[in] sel
603 * \return pixd
604 *
605 * <pre>
606 * Notes:
607 * (1) Generalized morphological closing, using both hits and
608 * misses in the Sel.
609 * (2) This does a dilation using the hits, followed by a
610 * hit-miss transform.
611 * (3) This operation is a dual of the generalized opening.
612 * (4) There are three cases:
613 * (a) pixd == null (result into new pixd)
614 * (b) pixd == pixs (in-place; writes result back to pixs)
615 * (c) pixd != pixs (puts result into existing pixd)
616 * (5) For clarity, if the case is known, use these patterns:
617 * (a) pixd = pixCloseGeneralized(NULL, pixs, ...);
618 * (b) pixCloseGeneralized(pixs, pixs, ...);
619 * (c) pixCloseGeneralized(pixd, pixs, ...);
620 * (6) The size of the result is determined by pixs.
621 * </pre>
622 */
623 PIX *
624 pixCloseGeneralized(PIX *pixd,
625 PIX *pixs,
626 SEL *sel)
627 {
628 PIX *pixt;
629
630 if ((pixd = processMorphArgs2(pixd, pixs, sel)) == NULL)
631 return (PIX *)ERROR_PTR("pixd not returned", __func__, pixd);
632
633 if ((pixt = pixDilate(NULL, pixs, sel)) == NULL)
634 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
635 pixHMT(pixd, pixt, sel);
636 pixDestroy(&pixt);
637
638 return pixd;
639 }
640
641
642 /*-----------------------------------------------------------------*
643 * Binary morphological (raster) ops with brick Sels *
644 *-----------------------------------------------------------------*/
645 /*!
646 * \brief pixDilateBrick()
647 *
648 * \param[in] pixd [optional]; this can be null, equal to pixs,
649 * or different from pixs
650 * \param[in] pixs 1 bpp
651 * \param[in] hsize width of brick Sel
652 * \param[in] vsize height of brick Sel
653 * \return pixd
654 *
655 * <pre>
656 * Notes:
657 * (1) Sel is a brick with all elements being hits
658 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
659 * (3) Do separably if both hsize and vsize are > 1.
660 * (4) There are three cases:
661 * (a) pixd == null (result into new pixd)
662 * (b) pixd == pixs (in-place; writes result back to pixs)
663 * (c) pixd != pixs (puts result into existing pixd)
664 * (5) For clarity, if the case is known, use these patterns:
665 * (a) pixd = pixDilateBrick(NULL, pixs, ...);
666 * (b) pixDilateBrick(pixs, pixs, ...);
667 * (c) pixDilateBrick(pixd, pixs, ...);
668 * (6) The size of the result is determined by pixs.
669 * </pre>
670 */
671 PIX *
672 pixDilateBrick(PIX *pixd,
673 PIX *pixs,
674 l_int32 hsize,
675 l_int32 vsize)
676 {
677 PIX *pixt;
678 SEL *sel, *selh, *selv;
679
680 if (!pixs)
681 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
682 if (pixGetDepth(pixs) != 1)
683 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
684 if (hsize < 1 || vsize < 1)
685 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
686
687 if (hsize == 1 && vsize == 1)
688 return pixCopy(pixd, pixs);
689 if (hsize == 1 || vsize == 1) { /* no intermediate result */
690 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
691 if (!sel)
692 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
693 pixd = pixDilate(pixd, pixs, sel);
694 selDestroy(&sel);
695 } else {
696 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
697 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
698 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
699 selDestroy(&selh);
700 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
701 }
702 pixt = pixDilate(NULL, pixs, selh);
703 pixd = pixDilate(pixd, pixt, selv);
704 pixDestroy(&pixt);
705 selDestroy(&selh);
706 selDestroy(&selv);
707 }
708
709 return pixd;
710 }
711
712
713 /*!
714 * \brief pixErodeBrick()
715 *
716 * \param[in] pixd [optional]; this can be null, equal to pixs,
717 * or different from pixs
718 * \param[in] pixs 1 bpp
719 * \param[in] hsize width of brick Sel
720 * \param[in] vsize height of brick Sel
721 * \return pixd
722 *
723 * <pre>
724 * Notes:
725 * (1) Sel is a brick with all elements being hits
726 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
727 * (3) Do separably if both hsize and vsize are > 1.
728 * (4) There are three cases:
729 * (a) pixd == null (result into new pixd)
730 * (b) pixd == pixs (in-place; writes result back to pixs)
731 * (c) pixd != pixs (puts result into existing pixd)
732 * (5) For clarity, if the case is known, use these patterns:
733 * (a) pixd = pixErodeBrick(NULL, pixs, ...);
734 * (b) pixErodeBrick(pixs, pixs, ...);
735 * (c) pixErodeBrick(pixd, pixs, ...);
736 * (6) The size of the result is determined by pixs.
737 * </pre>
738 */
739 PIX *
740 pixErodeBrick(PIX *pixd,
741 PIX *pixs,
742 l_int32 hsize,
743 l_int32 vsize)
744 {
745 PIX *pixt;
746 SEL *sel, *selh, *selv;
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
755 if (hsize == 1 && vsize == 1)
756 return pixCopy(pixd, pixs);
757 if (hsize == 1 || vsize == 1) { /* no intermediate result */
758 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
759 if (!sel)
760 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
761 pixd = pixErode(pixd, pixs, sel);
762 selDestroy(&sel);
763 } else {
764 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
765 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
766 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
767 selDestroy(&selh);
768 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
769 }
770 pixt = pixErode(NULL, pixs, selh);
771 pixd = pixErode(pixd, pixt, selv);
772 pixDestroy(&pixt);
773 selDestroy(&selh);
774 selDestroy(&selv);
775 }
776
777 return pixd;
778 }
779
780
781 /*!
782 * \brief pixOpenBrick()
783 *
784 * \param[in] pixd [optional]; this can be null, equal to pixs,
785 * or different from pixs
786 * \param[in] pixs 1 bpp
787 * \param[in] hsize width of brick Sel
788 * \param[in] vsize height of brick Sel
789 * \return pixd, or NULL on error
790 *
791 * <pre>
792 * Notes:
793 * (1) Sel is a brick with all elements being hits
794 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
795 * (3) Do separably if both hsize and vsize are > 1.
796 * (4) There are three cases:
797 * (a) pixd == null (result into new pixd)
798 * (b) pixd == pixs (in-place; writes result back to pixs)
799 * (c) pixd != pixs (puts result into existing pixd)
800 * (5) For clarity, if the case is known, use these patterns:
801 * (a) pixd = pixOpenBrick(NULL, pixs, ...);
802 * (b) pixOpenBrick(pixs, pixs, ...);
803 * (c) pixOpenBrick(pixd, pixs, ...);
804 * (6) The size of the result is determined by pixs.
805 * </pre>
806 */
807 PIX *
808 pixOpenBrick(PIX *pixd,
809 PIX *pixs,
810 l_int32 hsize,
811 l_int32 vsize)
812 {
813 PIX *pixt;
814 SEL *sel, *selh, *selv;
815
816 if (!pixs)
817 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
818 if (pixGetDepth(pixs) != 1)
819 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
820 if (hsize < 1 || vsize < 1)
821 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
822
823 if (hsize == 1 && vsize == 1)
824 return pixCopy(pixd, pixs);
825 if (hsize == 1 || vsize == 1) { /* no intermediate result */
826 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
827 if (!sel)
828 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
829 pixd = pixOpen(pixd, pixs, sel);
830 selDestroy(&sel);
831 } else { /* do separably */
832 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
833 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
834 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
835 selDestroy(&selh);
836 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
837 }
838 pixt = pixErode(NULL, pixs, selh);
839 pixd = pixErode(pixd, pixt, selv);
840 pixDilate(pixt, pixd, selh);
841 pixDilate(pixd, pixt, selv);
842 pixDestroy(&pixt);
843 selDestroy(&selh);
844 selDestroy(&selv);
845 }
846
847 return pixd;
848 }
849
850
851 /*!
852 * \brief pixCloseBrick()
853 *
854 * \param[in] pixd [optional]; this can be null, equal to pixs,
855 * or different from pixs
856 * \param[in] pixs 1 bpp
857 * \param[in] hsize width of brick Sel
858 * \param[in] vsize height of brick Sel
859 * \return pixd, or NULL on error
860 *
861 * <pre>
862 * Notes:
863 * (1) Sel is a brick with all elements being hits
864 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
865 * (3) Do separably if both hsize and vsize are > 1.
866 * (4) There are three cases:
867 * (a) pixd == null (result into new pixd)
868 * (b) pixd == pixs (in-place; writes result back to pixs)
869 * (c) pixd != pixs (puts result into existing pixd)
870 * (5) For clarity, if the case is known, use these patterns:
871 * (a) pixd = pixCloseBrick(NULL, pixs, ...);
872 * (b) pixCloseBrick(pixs, pixs, ...);
873 * (c) pixCloseBrick(pixd, pixs, ...);
874 * (6) The size of the result is determined by pixs.
875 * </pre>
876 */
877 PIX *
878 pixCloseBrick(PIX *pixd,
879 PIX *pixs,
880 l_int32 hsize,
881 l_int32 vsize)
882 {
883 PIX *pixt;
884 SEL *sel, *selh, *selv;
885
886 if (!pixs)
887 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
888 if (pixGetDepth(pixs) != 1)
889 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
890 if (hsize < 1 || vsize < 1)
891 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
892
893 if (hsize == 1 && vsize == 1)
894 return pixCopy(pixd, pixs);
895 if (hsize == 1 || vsize == 1) { /* no intermediate result */
896 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
897 if (!sel)
898 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
899 pixd = pixClose(pixd, pixs, sel);
900 selDestroy(&sel);
901 } else { /* do separably */
902 if ((selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT)) == NULL)
903 return (PIX *)ERROR_PTR("selh not made", __func__, pixd);
904 if ((selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT)) == NULL) {
905 selDestroy(&selh);
906 return (PIX *)ERROR_PTR("selv not made", __func__, pixd);
907 }
908 pixt = pixDilate(NULL, pixs, selh);
909 pixd = pixDilate(pixd, pixt, selv);
910 pixErode(pixt, pixd, selh);
911 pixErode(pixd, pixt, selv);
912 pixDestroy(&pixt);
913 selDestroy(&selh);
914 selDestroy(&selv);
915 }
916
917 return pixd;
918 }
919
920
921 /*!
922 * \brief pixCloseSafeBrick()
923 *
924 * \param[in] pixd [optional]; this can be null, equal to pixs,
925 * or different from pixs
926 * \param[in] pixs 1 bpp
927 * \param[in] hsize width of brick Sel
928 * \param[in] vsize height of brick Sel
929 * \return pixd, or NULL on error
930 *
931 * <pre>
932 * Notes:
933 * (1) Sel is a brick with all elements being hits
934 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
935 * (3) Do separably if both hsize and vsize are > 1.
936 * (4) Safe closing adds a border of 0 pixels, of sufficient size so
937 * that all pixels in input image are processed within
938 * 32-bit words in the expanded image. As a result, there is
939 * no special processing for pixels near the boundary, and there
940 * are no boundary effects. The border is removed at the end.
941 * (5) There are three cases:
942 * (a) pixd == null (result into new pixd)
943 * (b) pixd == pixs (in-place; writes result back to pixs)
944 * (c) pixd != pixs (puts result into existing pixd)
945 * (6) For clarity, if the case is known, use these patterns:
946 * (a) pixd = pixCloseBrick(NULL, pixs, ...);
947 * (b) pixCloseBrick(pixs, pixs, ...);
948 * (c) pixCloseBrick(pixd, pixs, ...);
949 * (7) The size of the result is determined by pixs.
950 * </pre>
951 */
952 PIX *
953 pixCloseSafeBrick(PIX *pixd,
954 PIX *pixs,
955 l_int32 hsize,
956 l_int32 vsize)
957 {
958 l_int32 maxtrans, bordsize;
959 PIX *pixsb, *pixt, *pixdb;
960 SEL *sel, *selh, *selv;
961
962 if (!pixs)
963 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
964 if (pixGetDepth(pixs) != 1)
965 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
966 if (hsize < 1 || vsize < 1)
967 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
968
969 if (hsize == 1 && vsize == 1)
970 return pixCopy(pixd, pixs);
971
972 /* Symmetric b.c. handles correctly without added pixels */
973 if (MORPH_BC == SYMMETRIC_MORPH_BC)
974 return pixCloseBrick(pixd, pixs, hsize, vsize);
975
976 maxtrans = L_MAX(hsize / 2, vsize / 2);
977 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
978 pixsb = pixAddBorder(pixs, bordsize, 0);
979
980 if (hsize == 1 || vsize == 1) { /* no intermediate result */
981 sel = selCreateBrick(vsize, hsize, vsize / 2, hsize / 2, SEL_HIT);
982 if (!sel) {
983 pixDestroy(&pixsb);
984 return (PIX *)ERROR_PTR("sel not made", __func__, pixd);
985 }
986 pixdb = pixClose(NULL, pixsb, sel);
987 selDestroy(&sel);
988 } else { /* do separably */
989 selh = selCreateBrick(1, hsize, 0, hsize / 2, SEL_HIT);
990 selv = selCreateBrick(vsize, 1, vsize / 2, 0, SEL_HIT);
991 if (!selh || !selv) {
992 selDestroy(&selh);
993 selDestroy(&selv);
994 pixDestroy(&pixsb);
995 return (PIX *)ERROR_PTR("selh and selv not both made",
996 __func__, pixd);
997 }
998 pixt = pixDilate(NULL, pixsb, selh);
999 pixdb = pixDilate(NULL, pixt, selv);
1000 pixErode(pixt, pixdb, selh);
1001 pixErode(pixdb, pixt, selv);
1002 pixDestroy(&pixt);
1003 selDestroy(&selh);
1004 selDestroy(&selv);
1005 }
1006
1007 pixt = pixRemoveBorder(pixdb, bordsize);
1008 pixDestroy(&pixsb);
1009 pixDestroy(&pixdb);
1010
1011 if (!pixd) {
1012 pixd = pixt;
1013 } else {
1014 pixCopy(pixd, pixt);
1015 pixDestroy(&pixt);
1016 }
1017 return pixd;
1018 }
1019
1020
1021 /*-----------------------------------------------------------------*
1022 * Binary composed morphological (raster) ops with brick Sels *
1023 *-----------------------------------------------------------------*/
1024 /* \brief selectComposableSels()
1025 *
1026 * \param[in] size of composed sel
1027 * \param[in] direction L_HORIZ, L_VERT
1028 * \param[out] psel1 [optional] contiguous sel; can be null
1029 * \param[out] psel2 [optional] comb sel; can be null
1030 * \return 0 if OK, 1 on error
1031 *
1032 * <pre>
1033 * Notes:
1034 * (1) When using composable Sels, where the original Sel is
1035 * decomposed into two, the best you can do in terms
1036 * of reducing the computation is by a factor:
1037 *
1038 * 2 * sqrt(size) / size
1039 *
1040 * In practice, you get quite close to this. E.g.,
1041 *
1042 * Sel size | Optimum reduction factor
1043 * -------- ------------------------
1044 * 36 | 1/3
1045 * 64 | 1/4
1046 * 144 | 1/6
1047 * 256 | 1/8
1048 * </pre>
1049 */
1050 l_int32
1051 selectComposableSels(l_int32 size,
1052 l_int32 direction,
1053 SEL **psel1,
1054 SEL **psel2)
1055 {
1056 l_int32 factor1, factor2;
1057
1058 if (!psel1 && !psel2)
1059 return ERROR_INT("neither &sel1 nor &sel2 are defined", __func__, 1);
1060 if (psel1) *psel1 = NULL;
1061 if (psel2) *psel2 = NULL;
1062 if (size < 1 || size > 10000)
1063 return ERROR_INT("size < 1 or size > 10000", __func__, 1);
1064 if (direction != L_HORIZ && direction != L_VERT)
1065 return ERROR_INT("invalid direction", __func__, 1);
1066
1067 if (selectComposableSizes(size, &factor1, &factor2))
1068 return ERROR_INT("factors not found", __func__, 1);
1069
1070 if (psel1) {
1071 if (direction == L_HORIZ)
1072 *psel1 = selCreateBrick(1, factor1, 0, factor1 / 2, SEL_HIT);
1073 else
1074 *psel1 = selCreateBrick(factor1, 1, factor1 / 2 , 0, SEL_HIT);
1075 }
1076 if (psel2)
1077 *psel2 = selCreateComb(factor1, factor2, direction);
1078 return 0;
1079 }
1080
1081
1082 /*!
1083 * \brief selectComposableSizes()
1084 *
1085 * \param[in] size of sel to be decomposed
1086 * \param[out] pfactor1 larger factor
1087 * \param[out] pfactor2 smaller factor
1088 * \return 0 if OK, 1 on error
1089 *
1090 * <pre>
1091 * Notes:
1092 * (1) This works for Sel sizes up to 10000, which seems sufficient.
1093 * (2) The composable sel size is typically within +- 1 of
1094 * the requested size. Up to size = 300, the maximum difference
1095 * is +- 2.
1096 * (3) We choose an overall cost function where the penalty for
1097 * the size difference between input and actual is 4 times
1098 * the penalty for additional rasterops.
1099 * (4) Returned values: factor1 >= factor2
1100 * If size > 1, then factor1 > 1.
1101 * </pre>
1102 */
1103 l_ok
1104 selectComposableSizes(l_int32 size,
1105 l_int32 *pfactor1,
1106 l_int32 *pfactor2)
1107 {
1108 l_int32 i, midval, val1, val2m, val2p;
1109 l_int32 index, prodm, prodp;
1110 l_int32 mincost, totcost, rastcostm, rastcostp, diffm, diffp;
1111 l_int32 lowval[256];
1112 l_int32 hival[256];
1113 l_int32 rastcost[256]; /* excess in sum of sizes (extra rasterops) */
1114 l_int32 diff[256]; /* diff between product (sel size) and input size */
1115
1116 if (size < 1 || size > 10000)
1117 return ERROR_INT("size < 1 or size > 10000", __func__, 1);
1118 if (!pfactor1 || !pfactor2)
1119 return ERROR_INT("&factor1 or &factor2 not defined", __func__, 1);
1120
1121 midval = (l_int32)(sqrt((l_float64)size) + 0.001);
1122 if (midval * midval == size) {
1123 *pfactor1 = *pfactor2 = midval;
1124 return 0;
1125 }
1126
1127 /* Set up arrays. For each val1, optimize for lowest diff,
1128 * and save the rastcost, the diff, and the two factors. */
1129 for (val1 = midval + 1, i = 0; val1 > 0; val1--, i++) {
1130 val2m = size / val1;
1131 val2p = val2m + 1;
1132 prodm = val1 * val2m;
1133 prodp = val1 * val2p;
1134 rastcostm = val1 + val2m - 2 * midval;
1135 rastcostp = val1 + val2p - 2 * midval;
1136 diffm = L_ABS(size - prodm);
1137 diffp = L_ABS(size - prodp);
1138 if (diffm <= diffp) {
1139 lowval[i] = L_MIN(val1, val2m);
1140 hival[i] = L_MAX(val1, val2m);
1141 rastcost[i] = rastcostm;
1142 diff[i] = diffm;
1143 } else {
1144 lowval[i] = L_MIN(val1, val2p);
1145 hival[i] = L_MAX(val1, val2p);
1146 rastcost[i] = rastcostp;
1147 diff[i] = diffp;
1148 }
1149 }
1150
1151 /* Choose the optimum factors; use cost ratio 4 on diff */
1152 mincost = 10000;
1153 index = 1; /* unimportant initial value */
1154 for (i = 0; i < midval + 1; i++) {
1155 if (diff[i] == 0 && rastcost[i] < ACCEPTABLE_COST) {
1156 *pfactor1 = hival[i];
1157 *pfactor2 = lowval[i];
1158 return 0;
1159 }
1160 totcost = 4 * diff[i] + rastcost[i];
1161 if (totcost < mincost) {
1162 mincost = totcost;
1163 index = i;
1164 }
1165 }
1166 *pfactor1 = hival[index];
1167 *pfactor2 = lowval[index];
1168
1169 return 0;
1170 }
1171
1172
1173 /*!
1174 * \brief pixDilateCompBrick()
1175 *
1176 * \param[in] pixd [optional]; this can be null, equal to pixs,
1177 * or different from pixs
1178 * \param[in] pixs 1 bpp
1179 * \param[in] hsize width of brick Sel
1180 * \param[in] vsize height of brick Sel
1181 * \return pixd, or NULL on error
1182 *
1183 * <pre>
1184 * Notes:
1185 * (1) Sel is a brick with all elements being hits
1186 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
1187 * (3) Do compositely for each dimension > 1.
1188 * (4) Do separably if both hsize and vsize are > 1.
1189 * (5) There are three cases:
1190 * (a) pixd == null (result into new pixd)
1191 * (b) pixd == pixs (in-place; writes result back to pixs)
1192 * (c) pixd != pixs (puts result into existing pixd)
1193 * (6) For clarity, if the case is known, use these patterns:
1194 * (a) pixd = pixDilateCompBrick(NULL, pixs, ...);
1195 * (b) pixDilateCompBrick(pixs, pixs, ...);
1196 * (c) pixDilateCompBrick(pixd, pixs, ...);
1197 * (7) The dimensions of the resulting image are determined by pixs.
1198 * (8) CAUTION: both hsize and vsize are being decomposed.
1199 * The decomposer chooses a product of sizes (call them
1200 * 'terms') for each that is close to the input size,
1201 * but not necessarily equal to it. It attempts to optimize:
1202 * (a) for consistency with the input values: the product
1203 * of terms is close to the input size
1204 * (b) for efficiency of the operation: the sum of the
1205 * terms is small; ideally about twice the square
1206 * root of the input size.
1207 * So, for example, if the input hsize = 37, which is
1208 * a prime number, the decomposer will break this into two
1209 * terms, 6 and 6, so that the net result is a dilation
1210 * with hsize = 36.
1211 * </pre>
1212 */
1213 PIX *
1214 pixDilateCompBrick(PIX *pixd,
1215 PIX *pixs,
1216 l_int32 hsize,
1217 l_int32 vsize)
1218 {
1219 PIX *pix1, *pix2, *pix3;
1220 SEL *selh1 = NULL;
1221 SEL *selh2 = NULL;
1222 SEL *selv1 = NULL;
1223 SEL *selv2 = NULL;
1224
1225 if (!pixs)
1226 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1227 if (pixGetDepth(pixs) != 1)
1228 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1229 if (hsize < 1 || vsize < 1)
1230 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1231
1232 if (hsize == 1 && vsize == 1)
1233 return pixCopy(pixd, pixs);
1234 if (hsize > 1) {
1235 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1236 selDestroy(&selh1);
1237 selDestroy(&selh2);
1238 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1239 }
1240 }
1241 if (vsize > 1) {
1242 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1243 selDestroy(&selh1);
1244 selDestroy(&selh2);
1245 selDestroy(&selv1);
1246 selDestroy(&selv2);
1247 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1248 }
1249 }
1250
1251 pix1 = pixAddBorder(pixs, 32, 0);
1252 if (vsize == 1) {
1253 pix2 = pixDilate(NULL, pix1, selh1);
1254 pix3 = pixDilate(NULL, pix2, selh2);
1255 } else if (hsize == 1) {
1256 pix2 = pixDilate(NULL, pix1, selv1);
1257 pix3 = pixDilate(NULL, pix2, selv2);
1258 } else {
1259 pix2 = pixDilate(NULL, pix1, selh1);
1260 pix3 = pixDilate(NULL, pix2, selh2);
1261 pixDilate(pix2, pix3, selv1);
1262 pixDilate(pix3, pix2, selv2);
1263 }
1264 pixDestroy(&pix1);
1265 pixDestroy(&pix2);
1266
1267 selDestroy(&selh1);
1268 selDestroy(&selh2);
1269 selDestroy(&selv1);
1270 selDestroy(&selv2);
1271
1272 pix1 = pixRemoveBorder(pix3, 32);
1273 pixDestroy(&pix3);
1274 if (!pixd)
1275 return pix1;
1276 pixCopy(pixd, pix1);
1277 pixDestroy(&pix1);
1278 return pixd;
1279 }
1280
1281
1282 /*!
1283 * \brief pixErodeCompBrick()
1284 *
1285 * \param[in] pixd [optional]; this can be null, equal to pixs,
1286 * or different from pixs
1287 * \param[in] pixs 1 bpp
1288 * \param[in] hsize width of brick Sel
1289 * \param[in] vsize height of brick Sel
1290 * \return pixd, or NULL on error
1291 *
1292 * <pre>
1293 * Notes:
1294 * (1) Sel is a brick with all elements being hits
1295 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
1296 * (3) Do compositely for each dimension > 1.
1297 * (4) Do separably if both hsize and vsize are > 1.
1298 * (5) There are three cases:
1299 * (a) pixd == null (result into new pixd)
1300 * (b) pixd == pixs (in-place; writes result back to pixs)
1301 * (c) pixd != pixs (puts result into existing pixd)
1302 * (6) For clarity, if the case is known, use these patterns:
1303 * (a) pixd = pixErodeCompBrick(NULL, pixs, ...);
1304 * (b) pixErodeCompBrick(pixs, pixs, ...);
1305 * (c) pixErodeCompBrick(pixd, pixs, ...);
1306 * (7) The dimensions of the resulting image are determined by pixs.
1307 * (8) CAUTION: both hsize and vsize are being decomposed.
1308 * The decomposer chooses a product of sizes (call them
1309 * 'terms') for each that is close to the input size,
1310 * but not necessarily equal to it. It attempts to optimize:
1311 * (a) for consistency with the input values: the product
1312 * of terms is close to the input size
1313 * (b) for efficiency of the operation: the sum of the
1314 * terms is small; ideally about twice the square
1315 * root of the input size.
1316 * So, for example, if the input hsize = 37, which is
1317 * a prime number, the decomposer will break this into two
1318 * terms, 6 and 6, so that the net result is a dilation
1319 * with hsize = 36.
1320 * </pre>
1321 */
1322 PIX *
1323 pixErodeCompBrick(PIX *pixd,
1324 PIX *pixs,
1325 l_int32 hsize,
1326 l_int32 vsize)
1327 {
1328 PIX *pixt;
1329 SEL *selh1 = NULL;
1330 SEL *selh2 = NULL;
1331 SEL *selv1 = NULL;
1332 SEL *selv2 = NULL;
1333
1334 if (!pixs)
1335 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1336 if (pixGetDepth(pixs) != 1)
1337 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1338 if (hsize < 1 || vsize < 1)
1339 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1340
1341 if (hsize == 1 && vsize == 1)
1342 return pixCopy(pixd, pixs);
1343 if (hsize > 1) {
1344 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1345 selDestroy(&selh1);
1346 selDestroy(&selh2);
1347 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1348 }
1349 }
1350 if (vsize > 1) {
1351 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1352 selDestroy(&selh1);
1353 selDestroy(&selh2);
1354 selDestroy(&selv1);
1355 selDestroy(&selv2);
1356 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1357 }
1358 }
1359
1360 if (vsize == 1) {
1361 pixt = pixErode(NULL, pixs, selh1);
1362 pixd = pixErode(pixd, pixt, selh2);
1363 } else if (hsize == 1) {
1364 pixt = pixErode(NULL, pixs, selv1);
1365 pixd = pixErode(pixd, pixt, selv2);
1366 } else {
1367 pixt = pixErode(NULL, pixs, selh1);
1368 pixd = pixErode(pixd, pixt, selh2);
1369 pixErode(pixt, pixd, selv1);
1370 pixErode(pixd, pixt, selv2);
1371 }
1372 pixDestroy(&pixt);
1373
1374 selDestroy(&selh1);
1375 selDestroy(&selh2);
1376 selDestroy(&selv1);
1377 selDestroy(&selv2);
1378 return pixd;
1379 }
1380
1381
1382 /*!
1383 * \brief pixOpenCompBrick()
1384 *
1385 * \param[in] pixd [optional]; this can be null, equal to pixs,
1386 * or different from pixs
1387 * \param[in] pixs 1 bpp
1388 * \param[in] hsize width of brick Sel
1389 * \param[in] vsize height of brick Sel
1390 * \return pixd, or NULL on error
1391 *
1392 * <pre>
1393 * Notes:
1394 * (1) Sel is a brick with all elements being hits
1395 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
1396 * (3) Do compositely for each dimension > 1.
1397 * (4) Do separably if both hsize and vsize are > 1.
1398 * (5) There are three cases:
1399 * (a) pixd == null (result into new pixd)
1400 * (b) pixd == pixs (in-place; writes result back to pixs)
1401 * (c) pixd != pixs (puts result into existing pixd)
1402 * (6) For clarity, if the case is known, use these patterns:
1403 * (a) pixd = pixOpenCompBrick(NULL, pixs, ...);
1404 * (b) pixOpenCompBrick(pixs, pixs, ...);
1405 * (c) pixOpenCompBrick(pixd, pixs, ...);
1406 * (7) The dimensions of the resulting image are determined by pixs.
1407 * (8) CAUTION: both hsize and vsize are being decomposed.
1408 * The decomposer chooses a product of sizes (call them
1409 * 'terms') for each that is close to the input size,
1410 * but not necessarily equal to it. It attempts to optimize:
1411 * (a) for consistency with the input values: the product
1412 * of terms is close to the input size
1413 * (b) for efficiency of the operation: the sum of the
1414 * terms is small; ideally about twice the square
1415 * root of the input size.
1416 * So, for example, if the input hsize = 37, which is
1417 * a prime number, the decomposer will break this into two
1418 * terms, 6 and 6, so that the net result is a dilation
1419 * with hsize = 36.
1420 * </pre>
1421 */
1422 PIX *
1423 pixOpenCompBrick(PIX *pixd,
1424 PIX *pixs,
1425 l_int32 hsize,
1426 l_int32 vsize)
1427 {
1428 PIX *pixt;
1429 SEL *selh1 = NULL;
1430 SEL *selh2 = NULL;
1431 SEL *selv1 = NULL;
1432 SEL *selv2 = NULL;
1433
1434 if (!pixs)
1435 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1436 if (pixGetDepth(pixs) != 1)
1437 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1438 if (hsize < 1 || vsize < 1)
1439 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1440
1441 if (hsize == 1 && vsize == 1)
1442 return pixCopy(pixd, pixs);
1443 if (hsize > 1) {
1444 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1445 selDestroy(&selh1);
1446 selDestroy(&selh2);
1447 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1448 }
1449 }
1450 if (vsize > 1) {
1451 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1452 selDestroy(&selh1);
1453 selDestroy(&selh2);
1454 selDestroy(&selv1);
1455 selDestroy(&selv2);
1456 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1457 }
1458 }
1459
1460 if (vsize == 1) {
1461 pixt = pixErode(NULL, pixs, selh1);
1462 pixd = pixErode(pixd, pixt, selh2);
1463 pixDilate(pixt, pixd, selh1);
1464 pixDilate(pixd, pixt, selh2);
1465 } else if (hsize == 1) {
1466 pixt = pixErode(NULL, pixs, selv1);
1467 pixd = pixErode(pixd, pixt, selv2);
1468 pixDilate(pixt, pixd, selv1);
1469 pixDilate(pixd, pixt, selv2);
1470 } else { /* do separably */
1471 pixt = pixErode(NULL, pixs, selh1);
1472 pixd = pixErode(pixd, pixt, selh2);
1473 pixErode(pixt, pixd, selv1);
1474 pixErode(pixd, pixt, selv2);
1475 pixDilate(pixt, pixd, selh1);
1476 pixDilate(pixd, pixt, selh2);
1477 pixDilate(pixt, pixd, selv1);
1478 pixDilate(pixd, pixt, selv2);
1479 }
1480 pixDestroy(&pixt);
1481
1482 selDestroy(&selh1);
1483 selDestroy(&selh2);
1484 selDestroy(&selv1);
1485 selDestroy(&selv2);
1486 return pixd;
1487 }
1488
1489
1490 /*!
1491 * \brief pixCloseCompBrick()
1492 *
1493 * \param[in] pixd [optional]; this can be null, equal to pixs,
1494 * or different from pixs
1495 * \param[in] pixs 1 bpp
1496 * \param[in] hsize width of brick Sel
1497 * \param[in] vsize height of brick Sel
1498 * \return pixd, or NULL on error
1499 *
1500 * <pre>
1501 * Notes:
1502 * (1) Sel is a brick with all elements being hits
1503 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
1504 * (3) Do compositely for each dimension > 1.
1505 * (4) Do separably if both hsize and vsize are > 1.
1506 * (5) There are three cases:
1507 * (a) pixd == null (result into new pixd)
1508 * (b) pixd == pixs (in-place; writes result back to pixs)
1509 * (c) pixd != pixs (puts result into existing pixd)
1510 * (6) For clarity, if the case is known, use these patterns:
1511 * (a) pixd = pixCloseCompBrick(NULL, pixs, ...);
1512 * (b) pixCloseCompBrick(pixs, pixs, ...);
1513 * (c) pixCloseCompBrick(pixd, pixs, ...);
1514 * (7) The dimensions of the resulting image are determined by pixs.
1515 * (8) CAUTION: both hsize and vsize are being decomposed.
1516 * The decomposer chooses a product of sizes (call them
1517 * 'terms') for each that is close to the input size,
1518 * but not necessarily equal to it. It attempts to optimize:
1519 * (a) for consistency with the input values: the product
1520 * of terms is close to the input size
1521 * (b) for efficiency of the operation: the sum of the
1522 * terms is small; ideally about twice the square
1523 * root of the input size.
1524 * So, for example, if the input hsize = 37, which is
1525 * a prime number, the decomposer will break this into two
1526 * terms, 6 and 6, so that the net result is a dilation
1527 * with hsize = 36.
1528 * </pre>
1529 */
1530 PIX *
1531 pixCloseCompBrick(PIX *pixd,
1532 PIX *pixs,
1533 l_int32 hsize,
1534 l_int32 vsize)
1535 {
1536 PIX *pixt;
1537 SEL *selh1 = NULL;
1538 SEL *selh2 = NULL;
1539 SEL *selv1 = NULL;
1540 SEL *selv2 = NULL;
1541
1542 if (!pixs)
1543 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1544 if (pixGetDepth(pixs) != 1)
1545 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1546 if (hsize < 1 || vsize < 1)
1547 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1548
1549 if (hsize == 1 && vsize == 1)
1550 return pixCopy(pixd, pixs);
1551 if (hsize > 1) {
1552 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1553 selDestroy(&selh1);
1554 selDestroy(&selh2);
1555 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1556 }
1557 }
1558 if (vsize > 1) {
1559 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1560 selDestroy(&selh1);
1561 selDestroy(&selh2);
1562 selDestroy(&selv1);
1563 selDestroy(&selv2);
1564 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1565 }
1566 }
1567
1568 if (vsize == 1) {
1569 pixt = pixDilate(NULL, pixs, selh1);
1570 pixd = pixDilate(pixd, pixt, selh2);
1571 pixErode(pixt, pixd, selh1);
1572 pixErode(pixd, pixt, selh2);
1573 } else if (hsize == 1) {
1574 pixt = pixDilate(NULL, pixs, selv1);
1575 pixd = pixDilate(pixd, pixt, selv2);
1576 pixErode(pixt, pixd, selv1);
1577 pixErode(pixd, pixt, selv2);
1578 } else { /* do separably */
1579 pixt = pixDilate(NULL, pixs, selh1);
1580 pixd = pixDilate(pixd, pixt, selh2);
1581 pixDilate(pixt, pixd, selv1);
1582 pixDilate(pixd, pixt, selv2);
1583 pixErode(pixt, pixd, selh1);
1584 pixErode(pixd, pixt, selh2);
1585 pixErode(pixt, pixd, selv1);
1586 pixErode(pixd, pixt, selv2);
1587 }
1588 pixDestroy(&pixt);
1589
1590 selDestroy(&selh1);
1591 selDestroy(&selh2);
1592 selDestroy(&selv1);
1593 selDestroy(&selv2);
1594 return pixd;
1595 }
1596
1597
1598 /*!
1599 * \brief pixCloseSafeCompBrick()
1600 *
1601 * \param[in] pixd [optional]; this can be null, equal to pixs,
1602 * or different from pixs
1603 * \param[in] pixs 1 bpp
1604 * \param[in] hsize width of brick Sel
1605 * \param[in] vsize height of brick Sel
1606 * \return pixd, or NULL on error
1607 *
1608 * <pre>
1609 * Notes:
1610 * (1) Sel is a brick with all elements being hits
1611 * (2) The origin is at (x, y) = (hsize/2, vsize/2)
1612 * (3) Do compositely for each dimension > 1.
1613 * (4) Do separably if both hsize and vsize are > 1.
1614 * (5) Safe closing adds a border of 0 pixels, of sufficient size so
1615 * that all pixels in input image are processed within
1616 * 32-bit words in the expanded image. As a result, there is
1617 * no special processing for pixels near the boundary, and there
1618 * are no boundary effects. The border is removed at the end.
1619 * (6) There are three cases:
1620 * (a) pixd == null (result into new pixd)
1621 * (b) pixd == pixs (in-place; writes result back to pixs)
1622 * (c) pixd != pixs (puts result into existing pixd)
1623 * (7) For clarity, if the case is known, use these patterns:
1624 * (a) pixd = pixCloseSafeCompBrick(NULL, pixs, ...);
1625 * (b) pixCloseSafeCompBrick(pixs, pixs, ...);
1626 * (c) pixCloseSafeCompBrick(pixd, pixs, ...);
1627 * (8) The dimensions of the resulting image are determined by pixs.
1628 * (9) CAUTION: both hsize and vsize are being decomposed.
1629 * The decomposer chooses a product of sizes (call them
1630 * 'terms') for each that is close to the input size,
1631 * but not necessarily equal to it. It attempts to optimize:
1632 * (a) for consistency with the input values: the product
1633 * of terms is close to the input size
1634 * (b) for efficiency of the operation: the sum of the
1635 * terms is small; ideally about twice the square
1636 * root of the input size.
1637 * So, for example, if the input hsize = 37, which is
1638 * a prime number, the decomposer will break this into two
1639 * terms, 6 and 6, so that the net result is a dilation
1640 * with hsize = 36.
1641 * </pre>
1642 */
1643 PIX *
1644 pixCloseSafeCompBrick(PIX *pixd,
1645 PIX *pixs,
1646 l_int32 hsize,
1647 l_int32 vsize)
1648 {
1649 l_int32 maxtrans, bordsize;
1650 PIX *pixsb, *pixt, *pixdb;
1651 SEL *selh1 = NULL;
1652 SEL *selh2 = NULL;
1653 SEL *selv1 = NULL;
1654 SEL *selv2 = NULL;
1655
1656 if (!pixs)
1657 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1658 if (pixGetDepth(pixs) != 1)
1659 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1660 if (hsize < 1 || vsize < 1)
1661 return (PIX *)ERROR_PTR("hsize and vsize not >= 1", __func__, pixd);
1662
1663 if (hsize == 1 && vsize == 1)
1664 return pixCopy(pixd, pixs);
1665
1666 /* Symmetric b.c. handles correctly without added pixels */
1667 if (MORPH_BC == SYMMETRIC_MORPH_BC)
1668 return pixCloseCompBrick(pixd, pixs, hsize, vsize);
1669
1670 if (hsize > 1) {
1671 if (selectComposableSels(hsize, L_HORIZ, &selh1, &selh2)) {
1672 selDestroy(&selh1);
1673 selDestroy(&selh2);
1674 return (PIX *)ERROR_PTR("horiz sels not made", __func__, pixd);
1675 }
1676 }
1677 if (vsize > 1) {
1678 if (selectComposableSels(vsize, L_VERT, &selv1, &selv2)) {
1679 selDestroy(&selh1);
1680 selDestroy(&selh2);
1681 selDestroy(&selv1);
1682 selDestroy(&selv2);
1683 return (PIX *)ERROR_PTR("vert sels not made", __func__, pixd);
1684 }
1685 }
1686
1687 maxtrans = L_MAX(hsize / 2, vsize / 2);
1688 bordsize = 32 * ((maxtrans + 31) / 32); /* full 32 bit words */
1689 pixsb = pixAddBorder(pixs, bordsize, 0);
1690
1691 if (vsize == 1) {
1692 pixt = pixDilate(NULL, pixsb, selh1);
1693 pixdb = pixDilate(NULL, pixt, selh2);
1694 pixErode(pixt, pixdb, selh1);
1695 pixErode(pixdb, pixt, selh2);
1696 } else if (hsize == 1) {
1697 pixt = pixDilate(NULL, pixsb, selv1);
1698 pixdb = pixDilate(NULL, pixt, selv2);
1699 pixErode(pixt, pixdb, selv1);
1700 pixErode(pixdb, pixt, selv2);
1701 } else { /* do separably */
1702 pixt = pixDilate(NULL, pixsb, selh1);
1703 pixdb = pixDilate(NULL, pixt, selh2);
1704 pixDilate(pixt, pixdb, selv1);
1705 pixDilate(pixdb, pixt, selv2);
1706 pixErode(pixt, pixdb, selh1);
1707 pixErode(pixdb, pixt, selh2);
1708 pixErode(pixt, pixdb, selv1);
1709 pixErode(pixdb, pixt, selv2);
1710 }
1711 pixDestroy(&pixt);
1712
1713 pixt = pixRemoveBorder(pixdb, bordsize);
1714 pixDestroy(&pixsb);
1715 pixDestroy(&pixdb);
1716
1717 if (!pixd) {
1718 pixd = pixt;
1719 } else {
1720 pixCopy(pixd, pixt);
1721 pixDestroy(&pixt);
1722 }
1723
1724 selDestroy(&selh1);
1725 selDestroy(&selh2);
1726 selDestroy(&selv1);
1727 selDestroy(&selv2);
1728 return pixd;
1729 }
1730
1731
1732 /*-----------------------------------------------------------------*
1733 * Functions associated with boundary conditions *
1734 *-----------------------------------------------------------------*/
1735 /*!
1736 * \brief resetMorphBoundaryCondition()
1737 *
1738 * \param[in] bc SYMMETRIC_MORPH_BC, ASYMMETRIC_MORPH_BC
1739 * \return void
1740 */
1741 void
1742 resetMorphBoundaryCondition(l_int32 bc)
1743 {
1744 if (bc != SYMMETRIC_MORPH_BC && bc != ASYMMETRIC_MORPH_BC) {
1745 L_WARNING("invalid bc; using asymmetric\n", __func__);
1746 bc = ASYMMETRIC_MORPH_BC;
1747 }
1748 MORPH_BC = bc;
1749 return;
1750 }
1751
1752
1753 /*!
1754 * \brief getMorphBorderPixelColor()
1755 *
1756 * \param[in] type L_MORPH_DILATE, L_MORPH_ERODE
1757 * \param[in] depth of pix
1758 * \return color of border pixels for this operation
1759 */
1760 l_uint32
1761 getMorphBorderPixelColor(l_int32 type,
1762 l_int32 depth)
1763 {
1764 if (type != L_MORPH_DILATE && type != L_MORPH_ERODE)
1765 return ERROR_INT("invalid type", __func__, 0);
1766 if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
1767 depth != 16 && depth != 32)
1768 return ERROR_INT("invalid depth", __func__, 0);
1769
1770 if (MORPH_BC == ASYMMETRIC_MORPH_BC || type == L_MORPH_DILATE)
1771 return 0;
1772
1773 /* Symmetric & erosion */
1774 if (depth < 32)
1775 return ((1 << depth) - 1);
1776 else /* depth == 32 */
1777 return 0xffffff00;
1778 }
1779
1780
1781 /*-----------------------------------------------------------------*
1782 * Static helpers for arg processing *
1783 *-----------------------------------------------------------------*/
1784 /*!
1785 * \brief processMorphArgs1()
1786 *
1787 * \param[in] pixd [optional]; this can be null, equal to pixs,
1788 * or different from pixs
1789 * \param[in] pixs 1 bpp
1790 * \param[in] sel
1791 * \param[out] ppixt copy or clone of %pixs
1792 * \return pixd, or NULL on error.
1793 *
1794 * <pre>
1795 * Notes:
1796 * (1) This is used for generic erosion, dilation and HMT.
1797 * </pre>
1798 */
1799 static PIX *
1800 processMorphArgs1(PIX *pixd,
1801 PIX *pixs,
1802 SEL *sel,
1803 PIX **ppixt)
1804 {
1805 l_int32 sx, sy;
1806
1807 if (!ppixt)
1808 return (PIX *)ERROR_PTR("&pixt not defined", __func__, pixd);
1809 *ppixt = NULL;
1810 if (!pixs)
1811 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1812 if (!sel)
1813 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
1814 if (pixGetDepth(pixs) != 1)
1815 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1816
1817 selGetParameters(sel, &sx, &sy, NULL, NULL);
1818 if (sx == 0 || sy == 0)
1819 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd);
1820
1821 /* We require pixd to exist and to be the same size as pixs.
1822 * Further, pixt must be a copy (or clone) of pixs. */
1823 if (!pixd) {
1824 if ((pixd = pixCreateTemplate(pixs)) == NULL)
1825 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1826 *ppixt = pixClone(pixs);
1827 } else {
1828 pixResizeImageData(pixd, pixs);
1829 if (pixd == pixs) { /* in-place; must make a copy of pixs */
1830 if ((*ppixt = pixCopy(NULL, pixs)) == NULL)
1831 return (PIX *)ERROR_PTR("pixt not made", __func__, pixd);
1832 } else {
1833 *ppixt = pixClone(pixs);
1834 }
1835 }
1836 return pixd;
1837 }
1838
1839
1840 /*!
1841 * \brief processMorphArgs2()
1842 *
1843 * This is used for generic openings and closings.
1844 */
1845 static PIX *
1846 processMorphArgs2(PIX *pixd,
1847 PIX *pixs,
1848 SEL *sel)
1849 {
1850 l_int32 sx, sy;
1851
1852 if (!pixs)
1853 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
1854 if (!sel)
1855 return (PIX *)ERROR_PTR("sel not defined", __func__, pixd);
1856 if (pixGetDepth(pixs) != 1)
1857 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd);
1858
1859 selGetParameters(sel, &sx, &sy, NULL, NULL);
1860 if (sx == 0 || sy == 0)
1861 return (PIX *)ERROR_PTR("sel of size 0", __func__, pixd);
1862
1863 if (!pixd)
1864 return pixCreateTemplate(pixs);
1865 pixResizeImageData(pixd, pixs);
1866 return pixd;
1867 }