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