Leptonica 1.84.1
Image processing and image analysis suite
Loading...
Searching...
No Matches
morphseq.c
Go to the documentation of this file.
1/*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
54#ifdef HAVE_CONFIG_H
55#include <config_auto.h>
56#endif /* HAVE_CONFIG_H */
57
58#include <string.h>
59#include "allheaders.h"
60
61/*-------------------------------------------------------------------------*
62 * Run a sequence of binary rasterop morphological operations *
63 *-------------------------------------------------------------------------*/
136PIX *
138 const char *sequence,
139 l_int32 dispsep)
140{
141char *rawop, *op;
142char fname[256];
143l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
144l_int32 level[4];
145PIX *pix1, *pix2;
146PIXA *pixa;
147SARRAY *sa;
148
149 if (!pixs)
150 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
151 if (!sequence)
152 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
153
154 /* Split sequence into individual operations */
155 sa = sarrayCreate(0);
156 sarraySplitString(sa, sequence, "+");
157 nops = sarrayGetCount(sa);
158 pdfout = (dispsep < 0) ? 1 : 0;
159 if (!morphSequenceVerify(sa)) {
160 sarrayDestroy(&sa);
161 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
162 }
163
164 /* Parse and operate */
165 pixa = NULL;
166 if (pdfout) {
167 pixa = pixaCreate(0);
168 pixaAddPix(pixa, pixs, L_CLONE);
169 }
170 border = 0;
171 pix1 = pixCopy(NULL, pixs);
172 pix2 = NULL;
173 x = 0;
174 for (i = 0; i < nops; i++) {
175 rawop = sarrayGetString(sa, i, L_NOCOPY);
176 op = stringRemoveChars(rawop, " \n\t");
177 switch (op[0])
178 {
179 case 'd':
180 case 'D':
181 sscanf(&op[1], "%d.%d", &w, &h);
182 pix2 = pixDilateBrick(NULL, pix1, w, h);
183 pixSwapAndDestroy(&pix1, &pix2);
184 break;
185 case 'e':
186 case 'E':
187 sscanf(&op[1], "%d.%d", &w, &h);
188 pix2 = pixErodeBrick(NULL, pix1, w, h);
189 pixSwapAndDestroy(&pix1, &pix2);
190 break;
191 case 'o':
192 case 'O':
193 sscanf(&op[1], "%d.%d", &w, &h);
194 pixOpenBrick(pix1, pix1, w, h);
195 break;
196 case 'c':
197 case 'C':
198 sscanf(&op[1], "%d.%d", &w, &h);
199 pixCloseSafeBrick(pix1, pix1, w, h);
200 break;
201 case 'r':
202 case 'R':
203 nred = strlen(op) - 1;
204 for (j = 0; j < nred; j++)
205 level[j] = op[j + 1] - '0';
206 for (j = nred; j < 4; j++)
207 level[j] = 0;
208 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
209 level[2], level[3]);
210 pixSwapAndDestroy(&pix1, &pix2);
211 break;
212 case 'x':
213 case 'X':
214 sscanf(&op[1], "%d", &fact);
215 pix2 = pixExpandReplicate(pix1, fact);
216 pixSwapAndDestroy(&pix1, &pix2);
217 break;
218 case 'b':
219 case 'B':
220 sscanf(&op[1], "%d", &border);
221 pix2 = pixAddBorder(pix1, border, 0);
222 pixSwapAndDestroy(&pix1, &pix2);
223 break;
224 default:
225 /* All invalid ops are caught in the first pass */
226 break;
227 }
228 LEPT_FREE(op);
229
230 /* Debug output */
231 if (dispsep > 0) {
232 pixDisplay(pix1, x, 0);
233 x += dispsep;
234 }
235 if (pdfout)
236 pixaAddPix(pixa, pix1, L_COPY);
237 }
238 if (border > 0) {
239 pix2 = pixRemoveBorder(pix1, border);
240 pixSwapAndDestroy(&pix1, &pix2);
241 }
242
243 if (pdfout) {
244 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf", L_ABS(dispsep));
245 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
246 pixaDestroy(&pixa);
247 }
248
249 sarrayDestroy(&sa);
250 return pix1;
251}
252
253
254/*-------------------------------------------------------------------------*
255 * Run a sequence of binary composite rasterop morphological operations *
256 *-------------------------------------------------------------------------*/
300PIX *
302 const char *sequence,
303 l_int32 dispsep)
304{
305char *rawop, *op;
306char fname[256];
307l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
308l_int32 level[4];
309PIX *pix1, *pix2;
310PIXA *pixa;
311SARRAY *sa;
312
313 if (!pixs)
314 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
315 if (!sequence)
316 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
317
318 /* Split sequence into individual operations */
319 sa = sarrayCreate(0);
320 sarraySplitString(sa, sequence, "+");
321 nops = sarrayGetCount(sa);
322 pdfout = (dispsep < 0) ? 1 : 0;
323
324 if (!morphSequenceVerify(sa)) {
325 sarrayDestroy(&sa);
326 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
327 }
328
329 /* Parse and operate */
330 pixa = NULL;
331 if (pdfout) {
332 pixa = pixaCreate(0);
333 pixaAddPix(pixa, pixs, L_CLONE);
334 }
335 border = 0;
336 pix1 = pixCopy(NULL, pixs);
337 pix2 = NULL;
338 x = 0;
339 for (i = 0; i < nops; i++) {
340 rawop = sarrayGetString(sa, i, L_NOCOPY);
341 op = stringRemoveChars(rawop, " \n\t");
342 switch (op[0])
343 {
344 case 'd':
345 case 'D':
346 sscanf(&op[1], "%d.%d", &w, &h);
347 pix2 = pixDilateCompBrick(NULL, pix1, w, h);
348 pixSwapAndDestroy(&pix1, &pix2);
349 break;
350 case 'e':
351 case 'E':
352 sscanf(&op[1], "%d.%d", &w, &h);
353 pix2 = pixErodeCompBrick(NULL, pix1, w, h);
354 pixSwapAndDestroy(&pix1, &pix2);
355 break;
356 case 'o':
357 case 'O':
358 sscanf(&op[1], "%d.%d", &w, &h);
359 pixOpenCompBrick(pix1, pix1, w, h);
360 break;
361 case 'c':
362 case 'C':
363 sscanf(&op[1], "%d.%d", &w, &h);
364 pixCloseSafeCompBrick(pix1, pix1, w, h);
365 break;
366 case 'r':
367 case 'R':
368 nred = strlen(op) - 1;
369 for (j = 0; j < nred; j++)
370 level[j] = op[j + 1] - '0';
371 for (j = nred; j < 4; j++)
372 level[j] = 0;
373 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
374 level[2], level[3]);
375 pixSwapAndDestroy(&pix1, &pix2);
376 break;
377 case 'x':
378 case 'X':
379 sscanf(&op[1], "%d", &fact);
380 pix2 = pixExpandReplicate(pix1, fact);
381 pixSwapAndDestroy(&pix1, &pix2);
382 break;
383 case 'b':
384 case 'B':
385 sscanf(&op[1], "%d", &border);
386 pix2 = pixAddBorder(pix1, border, 0);
387 pixSwapAndDestroy(&pix1, &pix2);
388 break;
389 default:
390 /* All invalid ops are caught in the first pass */
391 break;
392 }
393 LEPT_FREE(op);
394
395 /* Debug output */
396 if (dispsep > 0) {
397 pixDisplay(pix1, x, 0);
398 x += dispsep;
399 }
400 if (pdfout)
401 pixaAddPix(pixa, pix1, L_COPY);
402 }
403 if (border > 0) {
404 pix2 = pixRemoveBorder(pix1, border);
405 pixSwapAndDestroy(&pix1, &pix2);
406 }
407
408 if (pdfout) {
409 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
410 L_ABS(dispsep));
411 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
412 pixaDestroy(&pixa);
413 }
414
415 sarrayDestroy(&sa);
416 return pix1;
417}
418
419
420/*-------------------------------------------------------------------------*
421 * Run a sequence of binary dwa morphological operations *
422 *-------------------------------------------------------------------------*/
447PIX *
449 const char *sequence,
450 l_int32 dispsep)
451{
452char *rawop, *op;
453char fname[256];
454l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
455l_int32 level[4];
456PIX *pix1, *pix2;
457PIXA *pixa;
458SARRAY *sa;
459
460 if (!pixs)
461 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
462 if (!sequence)
463 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
464
465 /* Split sequence into individual operations */
466 sa = sarrayCreate(0);
467 sarraySplitString(sa, sequence, "+");
468 nops = sarrayGetCount(sa);
469 pdfout = (dispsep < 0) ? 1 : 0;
470
471 if (!morphSequenceVerify(sa)) {
472 sarrayDestroy(&sa);
473 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
474 }
475
476 /* Parse and operate */
477 pixa = NULL;
478 if (pdfout) {
479 pixa = pixaCreate(0);
480 pixaAddPix(pixa, pixs, L_CLONE);
481 }
482 border = 0;
483 pix1 = pixCopy(NULL, pixs);
484 pix2 = NULL;
485 x = 0;
486 for (i = 0; i < nops; i++) {
487 rawop = sarrayGetString(sa, i, L_NOCOPY);
488 op = stringRemoveChars(rawop, " \n\t");
489 switch (op[0])
490 {
491 case 'd':
492 case 'D':
493 sscanf(&op[1], "%d.%d", &w, &h);
494 pix2 = pixDilateBrickDwa(NULL, pix1, w, h);
495 pixSwapAndDestroy(&pix1, &pix2);
496 break;
497 case 'e':
498 case 'E':
499 sscanf(&op[1], "%d.%d", &w, &h);
500 pix2 = pixErodeBrickDwa(NULL, pix1, w, h);
501 pixSwapAndDestroy(&pix1, &pix2);
502 break;
503 case 'o':
504 case 'O':
505 sscanf(&op[1], "%d.%d", &w, &h);
506 pixOpenBrickDwa(pix1, pix1, w, h);
507 break;
508 case 'c':
509 case 'C':
510 sscanf(&op[1], "%d.%d", &w, &h);
511 pixCloseBrickDwa(pix1, pix1, w, h);
512 break;
513 case 'r':
514 case 'R':
515 nred = strlen(op) - 1;
516 for (j = 0; j < nred; j++)
517 level[j] = op[j + 1] - '0';
518 for (j = nred; j < 4; j++)
519 level[j] = 0;
520 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
521 level[2], level[3]);
522 pixSwapAndDestroy(&pix1, &pix2);
523 break;
524 case 'x':
525 case 'X':
526 sscanf(&op[1], "%d", &fact);
527 pix2 = pixExpandReplicate(pix1, fact);
528 pixSwapAndDestroy(&pix1, &pix2);
529 break;
530 case 'b':
531 case 'B':
532 sscanf(&op[1], "%d", &border);
533 pix2 = pixAddBorder(pix1, border, 0);
534 pixSwapAndDestroy(&pix1, &pix2);
535 break;
536 default:
537 /* All invalid ops are caught in the first pass */
538 break;
539 }
540 LEPT_FREE(op);
541
542 /* Debug output */
543 if (dispsep > 0) {
544 pixDisplay(pix1, x, 0);
545 x += dispsep;
546 }
547 if (pdfout)
548 pixaAddPix(pixa, pix1, L_COPY);
549 }
550 if (border > 0) {
551 pix2 = pixRemoveBorder(pix1, border);
552 pixSwapAndDestroy(&pix1, &pix2);
553 }
554
555 if (pdfout) {
556 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
557 L_ABS(dispsep));
558 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
559 pixaDestroy(&pixa);
560 }
561
562 sarrayDestroy(&sa);
563 return pix1;
564}
565
566
567/*-------------------------------------------------------------------------*
568 * Run a sequence of binary composite dwa morphological operations *
569 *-------------------------------------------------------------------------*/
594PIX *
596 const char *sequence,
597 l_int32 dispsep)
598{
599char *rawop, *op;
600char fname[256];
601l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
602l_int32 level[4];
603PIX *pix1, *pix2;
604PIXA *pixa;
605SARRAY *sa;
606
607 if (!pixs)
608 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
609 if (!sequence)
610 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
611
612 /* Split sequence into individual operations */
613 sa = sarrayCreate(0);
614 sarraySplitString(sa, sequence, "+");
615 nops = sarrayGetCount(sa);
616 pdfout = (dispsep < 0) ? 1 : 0;
617
618 if (!morphSequenceVerify(sa)) {
619 sarrayDestroy(&sa);
620 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
621 }
622
623 /* Parse and operate */
624 pixa = NULL;
625 if (pdfout) {
626 pixa = pixaCreate(0);
627 pixaAddPix(pixa, pixs, L_CLONE);
628 }
629 border = 0;
630 pix1 = pixCopy(NULL, pixs);
631 pix2 = NULL;
632 x = 0;
633 for (i = 0; i < nops; i++) {
634 rawop = sarrayGetString(sa, i, L_NOCOPY);
635 op = stringRemoveChars(rawop, " \n\t");
636 switch (op[0])
637 {
638 case 'd':
639 case 'D':
640 sscanf(&op[1], "%d.%d", &w, &h);
641 pix2 = pixDilateCompBrickDwa(NULL, pix1, w, h);
642 pixSwapAndDestroy(&pix1, &pix2);
643 break;
644 case 'e':
645 case 'E':
646 sscanf(&op[1], "%d.%d", &w, &h);
647 pix2 = pixErodeCompBrickDwa(NULL, pix1, w, h);
648 pixSwapAndDestroy(&pix1, &pix2);
649 break;
650 case 'o':
651 case 'O':
652 sscanf(&op[1], "%d.%d", &w, &h);
653 pixOpenCompBrickDwa(pix1, pix1, w, h);
654 break;
655 case 'c':
656 case 'C':
657 sscanf(&op[1], "%d.%d", &w, &h);
658 pixCloseCompBrickDwa(pix1, pix1, w, h);
659 break;
660 case 'r':
661 case 'R':
662 nred = strlen(op) - 1;
663 for (j = 0; j < nred; j++)
664 level[j] = op[j + 1] - '0';
665 for (j = nred; j < 4; j++)
666 level[j] = 0;
667 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
668 level[2], level[3]);
669 pixSwapAndDestroy(&pix1, &pix2);
670 break;
671 case 'x':
672 case 'X':
673 sscanf(&op[1], "%d", &fact);
674 pix2 = pixExpandReplicate(pix1, fact);
675 pixSwapAndDestroy(&pix1, &pix2);
676 break;
677 case 'b':
678 case 'B':
679 sscanf(&op[1], "%d", &border);
680 pix2 = pixAddBorder(pix1, border, 0);
681 pixSwapAndDestroy(&pix1, &pix2);
682 break;
683 default:
684 /* All invalid ops are caught in the first pass */
685 break;
686 }
687 LEPT_FREE(op);
688
689 /* Debug output */
690 if (dispsep > 0) {
691 pixDisplay(pix1, x, 0);
692 x += dispsep;
693 }
694 if (pdfout)
695 pixaAddPix(pixa, pix1, L_COPY);
696 }
697 if (border > 0) {
698 pix2 = pixRemoveBorder(pix1, border);
699 pixSwapAndDestroy(&pix1, &pix2);
700 }
701
702 if (pdfout) {
703 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
704 L_ABS(dispsep));
705 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
706 pixaDestroy(&pixa);
707 }
708
709 sarrayDestroy(&sa);
710 return pix1;
711}
712
713
714/*-------------------------------------------------------------------------*
715 * Parser verifier for binary morphological operations *
716 *-------------------------------------------------------------------------*/
731l_int32
733{
734char *rawop, *op = NULL;
735l_int32 nops, i, j, nred, fact, valid, w, h, netred, border;
736l_int32 level[4];
737l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */
738
739 if (!sa)
740 return ERROR_INT("sa not defined", __func__, FALSE);
741
742 nops = sarrayGetCount(sa);
743 valid = TRUE;
744 netred = 0;
745 border = 0;
746 for (i = 0; i < nops; i++) {
747 rawop = sarrayGetString(sa, i, L_NOCOPY);
748 op = stringRemoveChars(rawop, " \n\t");
749 switch (op[0])
750 {
751 case 'd':
752 case 'D':
753 case 'e':
754 case 'E':
755 case 'o':
756 case 'O':
757 case 'c':
758 case 'C':
759 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
760 lept_stderr("*** op: %s invalid\n", op);
761 valid = FALSE;
762 break;
763 }
764 if (w <= 0 || h <= 0) {
765 lept_stderr("*** op: %s; w = %d, h = %d; must both be > 0\n",
766 op, w, h);
767 valid = FALSE;
768 break;
769 }
770/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
771 break;
772 case 'r':
773 case 'R':
774 nred = strlen(op) - 1;
775 netred += nred;
776 if (nred < 1 || nred > 4) {
777 lept_stderr(
778 "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n",
779 op, nred);
780 valid = FALSE;
781 break;
782 }
783 for (j = 0; j < nred; j++) {
784 level[j] = op[j + 1] - '0';
785 if (level[j] < 1 || level[j] > 4) {
786 lept_stderr("*** op = %s; level[%d] = %d is invalid\n",
787 op, j, level[j]);
788 valid = FALSE;
789 break;
790 }
791 }
792 if (!valid)
793 break;
794/* lept_stderr("op = %s", op); */
795 for (j = 0; j < nred; j++) {
796 level[j] = op[j + 1] - '0';
797/* lept_stderr(", level[%d] = %d", j, level[j]); */
798 }
799/* lept_stderr("\n"); */
800 break;
801 case 'x':
802 case 'X':
803 if (sscanf(&op[1], "%d", &fact) != 1) {
804 lept_stderr("*** op: %s; fact invalid\n", op);
805 valid = FALSE;
806 break;
807 }
808 if (fact != 2 && fact != 4 && fact != 8 && fact != 16) {
809 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
810 valid = FALSE;
811 break;
812 }
813 netred -= intlogbase2[fact / 4];
814/* lept_stderr("op = %s; fact = %d\n", op, fact); */
815 break;
816 case 'b':
817 case 'B':
818 if (sscanf(&op[1], "%d", &fact) != 1) {
819 lept_stderr("*** op: %s; fact invalid\n", op);
820 valid = FALSE;
821 break;
822 }
823 if (i > 0) {
824 lept_stderr("*** op = %s; must be first op\n", op);
825 valid = FALSE;
826 break;
827 }
828 if (fact < 1) {
829 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
830 valid = FALSE;
831 break;
832 }
833 border = fact;
834/* lept_stderr("op = %s; fact = %d\n", op, fact); */
835 break;
836 default:
837 lept_stderr("*** nonexistent op = %s\n", op);
838 valid = FALSE;
839 }
840 LEPT_FREE(op);
841 }
842
843 if (border != 0 && netred != 0) {
844 lept_stderr("*** op = %s; border added but net reduction not 0\n", op);
845 valid = FALSE;
846 }
847 return valid;
848}
849
850
851/*-----------------------------------------------------------------*
852 * Run a sequence of grayscale morphological operations *
853 *-----------------------------------------------------------------*/
902PIX *
904 const char *sequence,
905 l_int32 dispsep,
906 l_int32 dispy)
907{
908char *rawop, *op;
909char fname[256];
910l_int32 nops, i, valid, w, h, x, pdfout;
911PIX *pix1, *pix2;
912PIXA *pixa;
913SARRAY *sa;
914
915 if (!pixs)
916 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
917 if (!sequence)
918 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
919
920 /* Split sequence into individual operations */
921 sa = sarrayCreate(0);
922 sarraySplitString(sa, sequence, "+");
923 nops = sarrayGetCount(sa);
924 pdfout = (dispsep < 0) ? 1 : 0;
925
926 /* Verify that the operation sequence is valid */
927 valid = TRUE;
928 for (i = 0; i < nops; i++) {
929 rawop = sarrayGetString(sa, i, L_NOCOPY);
930 op = stringRemoveChars(rawop, " \n\t");
931 switch (op[0])
932 {
933 case 'd':
934 case 'D':
935 case 'e':
936 case 'E':
937 case 'o':
938 case 'O':
939 case 'c':
940 case 'C':
941 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
942 lept_stderr("*** op: %s invalid\n", op);
943 valid = FALSE;
944 break;
945 }
946 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
947 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
948 op, w, h);
949 valid = FALSE;
950 break;
951 }
952/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
953 break;
954 case 't':
955 case 'T':
956 if (op[1] != 'w' && op[1] != 'W' &&
957 op[1] != 'b' && op[1] != 'B') {
958 lept_stderr(
959 "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]);
960 valid = FALSE;
961 break;
962 }
963 sscanf(&op[2], "%d.%d", &w, &h);
964 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
965 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
966 op, w, h);
967 valid = FALSE;
968 break;
969 }
970/* lept_stderr("op = %s", op); */
971 break;
972 default:
973 lept_stderr("*** nonexistent op = %s\n", op);
974 valid = FALSE;
975 }
976 LEPT_FREE(op);
977 }
978 if (!valid) {
979 sarrayDestroy(&sa);
980 return (PIX *)ERROR_PTR("sequence invalid", __func__, NULL);
981 }
982
983 /* Parse and operate */
984 pixa = NULL;
985 if (pdfout) {
986 pixa = pixaCreate(0);
987 pixaAddPix(pixa, pixs, L_CLONE);
988 }
989 pix1 = pixCopy(NULL, pixs);
990 pix2 = NULL;
991 x = 0;
992 for (i = 0; i < nops; i++) {
993 rawop = sarrayGetString(sa, i, L_NOCOPY);
994 op = stringRemoveChars(rawop, " \n\t");
995 switch (op[0])
996 {
997 case 'd':
998 case 'D':
999 sscanf(&op[1], "%d.%d", &w, &h);
1000 pix2 = pixDilateGray(pix1, w, h);
1001 pixSwapAndDestroy(&pix1, &pix2);
1002 break;
1003 case 'e':
1004 case 'E':
1005 sscanf(&op[1], "%d.%d", &w, &h);
1006 pix2 = pixErodeGray(pix1, w, h);
1007 pixSwapAndDestroy(&pix1, &pix2);
1008 break;
1009 case 'o':
1010 case 'O':
1011 sscanf(&op[1], "%d.%d", &w, &h);
1012 pix2 = pixOpenGray(pix1, w, h);
1013 pixSwapAndDestroy(&pix1, &pix2);
1014 break;
1015 case 'c':
1016 case 'C':
1017 sscanf(&op[1], "%d.%d", &w, &h);
1018 pix2 = pixCloseGray(pix1, w, h);
1019 pixSwapAndDestroy(&pix1, &pix2);
1020 break;
1021 case 't':
1022 case 'T':
1023 sscanf(&op[2], "%d.%d", &w, &h);
1024 if (op[1] == 'w' || op[1] == 'W')
1025 pix2 = pixTophat(pix1, w, h, L_TOPHAT_WHITE);
1026 else /* 'b' or 'B' */
1027 pix2 = pixTophat(pix1, w, h, L_TOPHAT_BLACK);
1028 pixSwapAndDestroy(&pix1, &pix2);
1029 break;
1030 default:
1031 /* All invalid ops are caught in the first pass */
1032 break;
1033 }
1034 LEPT_FREE(op);
1035
1036 /* Debug output */
1037 if (dispsep > 0) {
1038 pixDisplay(pix1, x, dispy);
1039 x += dispsep;
1040 }
1041 if (pdfout)
1042 pixaAddPix(pixa, pix1, L_COPY);
1043 }
1044
1045 if (pdfout) {
1046 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1047 L_ABS(dispsep));
1048 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1049 pixaDestroy(&pixa);
1050 }
1051
1052 sarrayDestroy(&sa);
1053 return pix1;
1054}
1055
1056
1057/*-----------------------------------------------------------------*
1058 * Run a sequence of color morphological operations *
1059 *-----------------------------------------------------------------*/
1103PIX *
1105 const char *sequence,
1106 l_int32 dispsep,
1107 l_int32 dispy)
1108{
1109char *rawop, *op;
1110char fname[256];
1111l_int32 nops, i, valid, w, h, x, pdfout;
1112PIX *pix1, *pix2;
1113PIXA *pixa;
1114SARRAY *sa;
1115
1116 if (!pixs)
1117 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1118 if (!sequence)
1119 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
1120
1121 /* Split sequence into individual operations */
1122 sa = sarrayCreate(0);
1123 sarraySplitString(sa, sequence, "+");
1124 nops = sarrayGetCount(sa);
1125 pdfout = (dispsep < 0) ? 1 : 0;
1126
1127 /* Verify that the operation sequence is valid */
1128 valid = TRUE;
1129 for (i = 0; i < nops; i++) {
1130 rawop = sarrayGetString(sa, i, L_NOCOPY);
1131 op = stringRemoveChars(rawop, " \n\t");
1132 switch (op[0])
1133 {
1134 case 'd':
1135 case 'D':
1136 case 'e':
1137 case 'E':
1138 case 'o':
1139 case 'O':
1140 case 'c':
1141 case 'C':
1142 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
1143 lept_stderr("*** op: %s invalid\n", op);
1144 valid = FALSE;
1145 break;
1146 }
1147 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
1148 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
1149 op, w, h);
1150 valid = FALSE;
1151 break;
1152 }
1153/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
1154 break;
1155 default:
1156 lept_stderr("*** nonexistent op = %s\n", op);
1157 valid = FALSE;
1158 }
1159 LEPT_FREE(op);
1160 }
1161 if (!valid) {
1162 sarrayDestroy(&sa);
1163 return (PIX *)ERROR_PTR("sequence invalid", __func__, NULL);
1164 }
1165
1166 /* Parse and operate */
1167 pixa = NULL;
1168 if (pdfout) {
1169 pixa = pixaCreate(0);
1170 pixaAddPix(pixa, pixs, L_CLONE);
1171 }
1172 pix1 = pixCopy(NULL, pixs);
1173 pix2 = NULL;
1174 x = 0;
1175 for (i = 0; i < nops; i++) {
1176 rawop = sarrayGetString(sa, i, L_NOCOPY);
1177 op = stringRemoveChars(rawop, " \n\t");
1178 switch (op[0])
1179 {
1180 case 'd':
1181 case 'D':
1182 sscanf(&op[1], "%d.%d", &w, &h);
1183 pix2 = pixColorMorph(pix1, L_MORPH_DILATE, w, h);
1184 pixSwapAndDestroy(&pix1, &pix2);
1185 break;
1186 case 'e':
1187 case 'E':
1188 sscanf(&op[1], "%d.%d", &w, &h);
1189 pix2 = pixColorMorph(pix1, L_MORPH_ERODE, w, h);
1190 pixSwapAndDestroy(&pix1, &pix2);
1191 break;
1192 case 'o':
1193 case 'O':
1194 sscanf(&op[1], "%d.%d", &w, &h);
1195 pix2 = pixColorMorph(pix1, L_MORPH_OPEN, w, h);
1196 pixSwapAndDestroy(&pix1, &pix2);
1197 break;
1198 case 'c':
1199 case 'C':
1200 sscanf(&op[1], "%d.%d", &w, &h);
1201 pix2 = pixColorMorph(pix1, L_MORPH_CLOSE, w, h);
1202 pixSwapAndDestroy(&pix1, &pix2);
1203 break;
1204 default:
1205 /* All invalid ops are caught in the first pass */
1206 break;
1207 }
1208 LEPT_FREE(op);
1209
1210 /* Debug output */
1211 if (dispsep > 0) {
1212 pixDisplay(pix1, x, dispy);
1213 x += dispsep;
1214 }
1215 if (pdfout)
1216 pixaAddPix(pixa, pix1, L_COPY);
1217 }
1218
1219 if (pdfout) {
1220 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1221 L_ABS(dispsep));
1222 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1223 pixaDestroy(&pixa);
1224 }
1225
1226 sarrayDestroy(&sa);
1227 return pix1;
1228}
@ L_FLATE_ENCODE
Definition imageio.h:161
PIX * pixMorphCompSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequence()
Definition morphseq.c:301
PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixColorMorphSequence()
Definition morphseq.c:1104
PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixGrayMorphSequence()
Definition morphseq.c:903
PIX * pixMorphCompSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequenceDwa()
Definition morphseq.c:595
PIX * pixMorphSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequenceDwa()
Definition morphseq.c:448
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition morphseq.c:137
l_int32 morphSequenceVerify(SARRAY *sa)
morphSequenceVerify()
Definition morphseq.c:732
@ L_COPY
Definition pix.h:505
@ L_CLONE
Definition pix.h:506
@ L_NOCOPY
Definition pix.h:503