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