Leptonica 1.85.0
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",
245 L_ABS(dispsep));
246 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
247 pixaDestroy(&pixa);
248 }
249
250 sarrayDestroy(&sa);
251 return pix1;
252}
253
254
255/*-------------------------------------------------------------------------*
256 * Run a sequence of binary composite rasterop morphological operations *
257 *-------------------------------------------------------------------------*/
301PIX *
303 const char *sequence,
304 l_int32 dispsep)
305{
306char *rawop, *op;
307char fname[256];
308l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
309l_int32 level[4];
310PIX *pix1, *pix2;
311PIXA *pixa;
312SARRAY *sa;
313
314 if (!pixs)
315 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
316 if (!sequence)
317 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
318
319 /* Split sequence into individual operations */
320 sa = sarrayCreate(0);
321 sarraySplitString(sa, sequence, "+");
322 nops = sarrayGetCount(sa);
323 pdfout = (dispsep < 0) ? 1 : 0;
324
325 if (!morphSequenceVerify(sa)) {
326 sarrayDestroy(&sa);
327 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
328 }
329
330 /* Parse and operate */
331 pixa = NULL;
332 if (pdfout) {
333 pixa = pixaCreate(0);
334 pixaAddPix(pixa, pixs, L_CLONE);
335 }
336 border = 0;
337 pix1 = pixCopy(NULL, pixs);
338 pix2 = NULL;
339 x = 0;
340 for (i = 0; i < nops; i++) {
341 rawop = sarrayGetString(sa, i, L_NOCOPY);
342 op = stringRemoveChars(rawop, " \n\t");
343 switch (op[0])
344 {
345 case 'd':
346 case 'D':
347 sscanf(&op[1], "%d.%d", &w, &h);
348 pix2 = pixDilateCompBrick(NULL, pix1, w, h);
349 pixSwapAndDestroy(&pix1, &pix2);
350 break;
351 case 'e':
352 case 'E':
353 sscanf(&op[1], "%d.%d", &w, &h);
354 pix2 = pixErodeCompBrick(NULL, pix1, w, h);
355 pixSwapAndDestroy(&pix1, &pix2);
356 break;
357 case 'o':
358 case 'O':
359 sscanf(&op[1], "%d.%d", &w, &h);
360 pixOpenCompBrick(pix1, pix1, w, h);
361 break;
362 case 'c':
363 case 'C':
364 sscanf(&op[1], "%d.%d", &w, &h);
365 pixCloseSafeCompBrick(pix1, pix1, w, h);
366 break;
367 case 'r':
368 case 'R':
369 nred = strlen(op) - 1;
370 for (j = 0; j < nred; j++)
371 level[j] = op[j + 1] - '0';
372 for (j = nred; j < 4; j++)
373 level[j] = 0;
374 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
375 level[2], level[3]);
376 pixSwapAndDestroy(&pix1, &pix2);
377 break;
378 case 'x':
379 case 'X':
380 sscanf(&op[1], "%d", &fact);
381 pix2 = pixExpandReplicate(pix1, fact);
382 pixSwapAndDestroy(&pix1, &pix2);
383 break;
384 case 'b':
385 case 'B':
386 sscanf(&op[1], "%d", &border);
387 pix2 = pixAddBorder(pix1, border, 0);
388 pixSwapAndDestroy(&pix1, &pix2);
389 break;
390 default:
391 /* All invalid ops are caught in the first pass */
392 break;
393 }
394 LEPT_FREE(op);
395
396 /* Debug output */
397 if (dispsep > 0) {
398 pixDisplay(pix1, x, 0);
399 x += dispsep;
400 }
401 if (pdfout)
402 pixaAddPix(pixa, pix1, L_COPY);
403 }
404 if (border > 0) {
405 pix2 = pixRemoveBorder(pix1, border);
406 pixSwapAndDestroy(&pix1, &pix2);
407 }
408
409 if (pdfout) {
410 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
411 L_ABS(dispsep));
412 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
413 pixaDestroy(&pixa);
414 }
415
416 sarrayDestroy(&sa);
417 return pix1;
418}
419
420
421/*-------------------------------------------------------------------------*
422 * Run a sequence of binary dwa morphological operations *
423 *-------------------------------------------------------------------------*/
448PIX *
450 const char *sequence,
451 l_int32 dispsep)
452{
453char *rawop, *op;
454char fname[256];
455l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
456l_int32 level[4];
457PIX *pix1, *pix2;
458PIXA *pixa;
459SARRAY *sa;
460
461 if (!pixs)
462 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
463 if (!sequence)
464 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
465
466 /* Split sequence into individual operations */
467 sa = sarrayCreate(0);
468 sarraySplitString(sa, sequence, "+");
469 nops = sarrayGetCount(sa);
470 pdfout = (dispsep < 0) ? 1 : 0;
471
472 if (!morphSequenceVerify(sa)) {
473 sarrayDestroy(&sa);
474 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
475 }
476
477 /* Parse and operate */
478 pixa = NULL;
479 if (pdfout) {
480 pixa = pixaCreate(0);
481 pixaAddPix(pixa, pixs, L_CLONE);
482 }
483 border = 0;
484 pix1 = pixCopy(NULL, pixs);
485 pix2 = NULL;
486 x = 0;
487 for (i = 0; i < nops; i++) {
488 rawop = sarrayGetString(sa, i, L_NOCOPY);
489 op = stringRemoveChars(rawop, " \n\t");
490 switch (op[0])
491 {
492 case 'd':
493 case 'D':
494 sscanf(&op[1], "%d.%d", &w, &h);
495 pix2 = pixDilateBrickDwa(NULL, pix1, w, h);
496 pixSwapAndDestroy(&pix1, &pix2);
497 break;
498 case 'e':
499 case 'E':
500 sscanf(&op[1], "%d.%d", &w, &h);
501 pix2 = pixErodeBrickDwa(NULL, pix1, w, h);
502 pixSwapAndDestroy(&pix1, &pix2);
503 break;
504 case 'o':
505 case 'O':
506 sscanf(&op[1], "%d.%d", &w, &h);
507 pixOpenBrickDwa(pix1, pix1, w, h);
508 break;
509 case 'c':
510 case 'C':
511 sscanf(&op[1], "%d.%d", &w, &h);
512 pixCloseBrickDwa(pix1, pix1, w, h);
513 break;
514 case 'r':
515 case 'R':
516 nred = strlen(op) - 1;
517 for (j = 0; j < nred; j++)
518 level[j] = op[j + 1] - '0';
519 for (j = nred; j < 4; j++)
520 level[j] = 0;
521 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
522 level[2], level[3]);
523 pixSwapAndDestroy(&pix1, &pix2);
524 break;
525 case 'x':
526 case 'X':
527 sscanf(&op[1], "%d", &fact);
528 pix2 = pixExpandReplicate(pix1, fact);
529 pixSwapAndDestroy(&pix1, &pix2);
530 break;
531 case 'b':
532 case 'B':
533 sscanf(&op[1], "%d", &border);
534 pix2 = pixAddBorder(pix1, border, 0);
535 pixSwapAndDestroy(&pix1, &pix2);
536 break;
537 default:
538 /* All invalid ops are caught in the first pass */
539 break;
540 }
541 LEPT_FREE(op);
542
543 /* Debug output */
544 if (dispsep > 0) {
545 pixDisplay(pix1, x, 0);
546 x += dispsep;
547 }
548 if (pdfout)
549 pixaAddPix(pixa, pix1, L_COPY);
550 }
551 if (border > 0) {
552 pix2 = pixRemoveBorder(pix1, border);
553 pixSwapAndDestroy(&pix1, &pix2);
554 }
555
556 if (pdfout) {
557 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
558 L_ABS(dispsep));
559 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
560 pixaDestroy(&pixa);
561 }
562
563 sarrayDestroy(&sa);
564 return pix1;
565}
566
567
568/*-------------------------------------------------------------------------*
569 * Run a sequence of binary composite dwa morphological operations *
570 *-------------------------------------------------------------------------*/
595PIX *
597 const char *sequence,
598 l_int32 dispsep)
599{
600char *rawop, *op;
601char fname[256];
602l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
603l_int32 level[4];
604PIX *pix1, *pix2;
605PIXA *pixa;
606SARRAY *sa;
607
608 if (!pixs)
609 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
610 if (!sequence)
611 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
612
613 /* Split sequence into individual operations */
614 sa = sarrayCreate(0);
615 sarraySplitString(sa, sequence, "+");
616 nops = sarrayGetCount(sa);
617 pdfout = (dispsep < 0) ? 1 : 0;
618
619 if (!morphSequenceVerify(sa)) {
620 sarrayDestroy(&sa);
621 return (PIX *)ERROR_PTR("sequence not valid", __func__, NULL);
622 }
623
624 /* Parse and operate */
625 pixa = NULL;
626 if (pdfout) {
627 pixa = pixaCreate(0);
628 pixaAddPix(pixa, pixs, L_CLONE);
629 }
630 border = 0;
631 pix1 = pixCopy(NULL, pixs);
632 pix2 = NULL;
633 x = 0;
634 for (i = 0; i < nops; i++) {
635 rawop = sarrayGetString(sa, i, L_NOCOPY);
636 op = stringRemoveChars(rawop, " \n\t");
637 switch (op[0])
638 {
639 case 'd':
640 case 'D':
641 sscanf(&op[1], "%d.%d", &w, &h);
642 pix2 = pixDilateCompBrickDwa(NULL, pix1, w, h);
643 pixSwapAndDestroy(&pix1, &pix2);
644 break;
645 case 'e':
646 case 'E':
647 sscanf(&op[1], "%d.%d", &w, &h);
648 pix2 = pixErodeCompBrickDwa(NULL, pix1, w, h);
649 pixSwapAndDestroy(&pix1, &pix2);
650 break;
651 case 'o':
652 case 'O':
653 sscanf(&op[1], "%d.%d", &w, &h);
654 pixOpenCompBrickDwa(pix1, pix1, w, h);
655 break;
656 case 'c':
657 case 'C':
658 sscanf(&op[1], "%d.%d", &w, &h);
659 pixCloseCompBrickDwa(pix1, pix1, w, h);
660 break;
661 case 'r':
662 case 'R':
663 nred = strlen(op) - 1;
664 for (j = 0; j < nred; j++)
665 level[j] = op[j + 1] - '0';
666 for (j = nred; j < 4; j++)
667 level[j] = 0;
668 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
669 level[2], level[3]);
670 pixSwapAndDestroy(&pix1, &pix2);
671 break;
672 case 'x':
673 case 'X':
674 sscanf(&op[1], "%d", &fact);
675 pix2 = pixExpandReplicate(pix1, fact);
676 pixSwapAndDestroy(&pix1, &pix2);
677 break;
678 case 'b':
679 case 'B':
680 sscanf(&op[1], "%d", &border);
681 pix2 = pixAddBorder(pix1, border, 0);
682 pixSwapAndDestroy(&pix1, &pix2);
683 break;
684 default:
685 /* All invalid ops are caught in the first pass */
686 break;
687 }
688 LEPT_FREE(op);
689
690 /* Debug output */
691 if (dispsep > 0) {
692 pixDisplay(pix1, x, 0);
693 x += dispsep;
694 }
695 if (pdfout)
696 pixaAddPix(pixa, pix1, L_COPY);
697 }
698 if (border > 0) {
699 pix2 = pixRemoveBorder(pix1, border);
700 pixSwapAndDestroy(&pix1, &pix2);
701 }
702
703 if (pdfout) {
704 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
705 L_ABS(dispsep));
706 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
707 pixaDestroy(&pixa);
708 }
709
710 sarrayDestroy(&sa);
711 return pix1;
712}
713
714
715/*-------------------------------------------------------------------------*
716 * Parser verifier for binary morphological operations *
717 *-------------------------------------------------------------------------*/
732l_int32
734{
735char *rawop, *op = NULL;
736l_int32 nops, i, j, nred, fact, valid, w, h, netred, border;
737l_int32 level[4];
738l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */
739
740 if (!sa)
741 return ERROR_INT("sa not defined", __func__, FALSE);
742
743 nops = sarrayGetCount(sa);
744 valid = TRUE;
745 netred = 0;
746 border = 0;
747 for (i = 0; i < nops; i++) {
748 rawop = sarrayGetString(sa, i, L_NOCOPY);
749 op = stringRemoveChars(rawop, " \n\t");
750 switch (op[0])
751 {
752 case 'd':
753 case 'D':
754 case 'e':
755 case 'E':
756 case 'o':
757 case 'O':
758 case 'c':
759 case 'C':
760 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
761 lept_stderr("*** op: %s invalid\n", op);
762 valid = FALSE;
763 break;
764 }
765 if (w <= 0 || h <= 0) {
766 lept_stderr("*** op: %s; w = %d, h = %d; must both be > 0\n",
767 op, w, h);
768 valid = FALSE;
769 break;
770 }
771/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
772 break;
773 case 'r':
774 case 'R':
775 nred = strlen(op) - 1;
776 netred += nred;
777 if (nred < 1 || nred > 4) {
778 lept_stderr(
779 "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n",
780 op, nred);
781 valid = FALSE;
782 break;
783 }
784 for (j = 0; j < nred; j++) {
785 level[j] = op[j + 1] - '0';
786 if (level[j] < 1 || level[j] > 4) {
787 lept_stderr("*** op = %s; level[%d] = %d is invalid\n",
788 op, j, level[j]);
789 valid = FALSE;
790 break;
791 }
792 }
793 if (!valid)
794 break;
795/* lept_stderr("op = %s", op); */
796 for (j = 0; j < nred; j++) {
797 level[j] = op[j + 1] - '0';
798/* lept_stderr(", level[%d] = %d", j, level[j]); */
799 }
800/* lept_stderr("\n"); */
801 break;
802 case 'x':
803 case 'X':
804 if (sscanf(&op[1], "%d", &fact) != 1) {
805 lept_stderr("*** op: %s; fact invalid\n", op);
806 valid = FALSE;
807 break;
808 }
809 if (fact != 2 && fact != 4 && fact != 8 && fact != 16) {
810 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
811 valid = FALSE;
812 break;
813 }
814 netred -= intlogbase2[fact / 4];
815/* lept_stderr("op = %s; fact = %d\n", op, fact); */
816 break;
817 case 'b':
818 case 'B':
819 if (sscanf(&op[1], "%d", &fact) != 1) {
820 lept_stderr("*** op: %s; fact invalid\n", op);
821 valid = FALSE;
822 break;
823 }
824 if (i > 0) {
825 lept_stderr("*** op = %s; must be first op\n", op);
826 valid = FALSE;
827 break;
828 }
829 if (fact < 1) {
830 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
831 valid = FALSE;
832 break;
833 }
834 border = fact;
835/* lept_stderr("op = %s; fact = %d\n", op, fact); */
836 break;
837 default:
838 lept_stderr("*** nonexistent op = %s\n", op);
839 valid = FALSE;
840 }
841 LEPT_FREE(op);
842 }
843
844 if (border != 0 && netred != 0) {
845 lept_stderr("*** op = %s; border added but net reduction not 0\n", op);
846 valid = FALSE;
847 }
848 return valid;
849}
850
851
852/*-----------------------------------------------------------------*
853 * Run a sequence of grayscale morphological operations *
854 *-----------------------------------------------------------------*/
903PIX *
905 const char *sequence,
906 l_int32 dispsep,
907 l_int32 dispy)
908{
909char *rawop, *op;
910char fname[256];
911l_int32 nops, i, valid, w, h, x, pdfout;
912PIX *pix1, *pix2;
913PIXA *pixa;
914SARRAY *sa;
915
916 if (!pixs)
917 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
918 if (!sequence)
919 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
920
921 /* Split sequence into individual operations */
922 sa = sarrayCreate(0);
923 sarraySplitString(sa, sequence, "+");
924 nops = sarrayGetCount(sa);
925 pdfout = (dispsep < 0) ? 1 : 0;
926
927 /* Verify that the operation sequence is valid */
928 valid = TRUE;
929 for (i = 0; i < nops; i++) {
930 rawop = sarrayGetString(sa, i, L_NOCOPY);
931 op = stringRemoveChars(rawop, " \n\t");
932 switch (op[0])
933 {
934 case 'd':
935 case 'D':
936 case 'e':
937 case 'E':
938 case 'o':
939 case 'O':
940 case 'c':
941 case 'C':
942 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
943 lept_stderr("*** op: %s invalid\n", op);
944 valid = FALSE;
945 break;
946 }
947 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
948 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
949 op, w, h);
950 valid = FALSE;
951 break;
952 }
953/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
954 break;
955 case 't':
956 case 'T':
957 if (op[1] != 'w' && op[1] != 'W' &&
958 op[1] != 'b' && op[1] != 'B') {
959 lept_stderr(
960 "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]);
961 valid = FALSE;
962 break;
963 }
964 sscanf(&op[2], "%d.%d", &w, &h);
965 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
966 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
967 op, w, h);
968 valid = FALSE;
969 break;
970 }
971/* lept_stderr("op = %s", op); */
972 break;
973 default:
974 lept_stderr("*** nonexistent op = %s\n", op);
975 valid = FALSE;
976 }
977 LEPT_FREE(op);
978 }
979 if (!valid) {
980 sarrayDestroy(&sa);
981 return (PIX *)ERROR_PTR("sequence invalid", __func__, NULL);
982 }
983
984 /* Parse and operate */
985 pixa = NULL;
986 if (pdfout) {
987 pixa = pixaCreate(0);
988 pixaAddPix(pixa, pixs, L_CLONE);
989 }
990 pix1 = pixCopy(NULL, pixs);
991 pix2 = NULL;
992 x = 0;
993 for (i = 0; i < nops; i++) {
994 rawop = sarrayGetString(sa, i, L_NOCOPY);
995 op = stringRemoveChars(rawop, " \n\t");
996 switch (op[0])
997 {
998 case 'd':
999 case 'D':
1000 sscanf(&op[1], "%d.%d", &w, &h);
1001 pix2 = pixDilateGray(pix1, w, h);
1002 pixSwapAndDestroy(&pix1, &pix2);
1003 break;
1004 case 'e':
1005 case 'E':
1006 sscanf(&op[1], "%d.%d", &w, &h);
1007 pix2 = pixErodeGray(pix1, w, h);
1008 pixSwapAndDestroy(&pix1, &pix2);
1009 break;
1010 case 'o':
1011 case 'O':
1012 sscanf(&op[1], "%d.%d", &w, &h);
1013 pix2 = pixOpenGray(pix1, w, h);
1014 pixSwapAndDestroy(&pix1, &pix2);
1015 break;
1016 case 'c':
1017 case 'C':
1018 sscanf(&op[1], "%d.%d", &w, &h);
1019 pix2 = pixCloseGray(pix1, w, h);
1020 pixSwapAndDestroy(&pix1, &pix2);
1021 break;
1022 case 't':
1023 case 'T':
1024 sscanf(&op[2], "%d.%d", &w, &h);
1025 if (op[1] == 'w' || op[1] == 'W')
1026 pix2 = pixTophat(pix1, w, h, L_TOPHAT_WHITE);
1027 else /* 'b' or 'B' */
1028 pix2 = pixTophat(pix1, w, h, L_TOPHAT_BLACK);
1029 pixSwapAndDestroy(&pix1, &pix2);
1030 break;
1031 default:
1032 /* All invalid ops are caught in the first pass */
1033 break;
1034 }
1035 LEPT_FREE(op);
1036
1037 /* Debug output */
1038 if (dispsep > 0) {
1039 pixDisplay(pix1, x, dispy);
1040 x += dispsep;
1041 }
1042 if (pdfout)
1043 pixaAddPix(pixa, pix1, L_COPY);
1044 }
1045
1046 if (pdfout) {
1047 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1048 L_ABS(dispsep));
1049 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1050 pixaDestroy(&pixa);
1051 }
1052
1053 sarrayDestroy(&sa);
1054 return pix1;
1055}
1056
1057
1058/*-----------------------------------------------------------------*
1059 * Run a sequence of color morphological operations *
1060 *-----------------------------------------------------------------*/
1104PIX *
1106 const char *sequence,
1107 l_int32 dispsep,
1108 l_int32 dispy)
1109{
1110char *rawop, *op;
1111char fname[256];
1112l_int32 nops, i, valid, w, h, x, pdfout;
1113PIX *pix1, *pix2;
1114PIXA *pixa;
1115SARRAY *sa;
1116
1117 if (!pixs)
1118 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1119 if (!sequence)
1120 return (PIX *)ERROR_PTR("sequence not defined", __func__, NULL);
1121
1122 /* Split sequence into individual operations */
1123 sa = sarrayCreate(0);
1124 sarraySplitString(sa, sequence, "+");
1125 nops = sarrayGetCount(sa);
1126 pdfout = (dispsep < 0) ? 1 : 0;
1127
1128 /* Verify that the operation sequence is valid */
1129 valid = TRUE;
1130 for (i = 0; i < nops; i++) {
1131 rawop = sarrayGetString(sa, i, L_NOCOPY);
1132 op = stringRemoveChars(rawop, " \n\t");
1133 switch (op[0])
1134 {
1135 case 'd':
1136 case 'D':
1137 case 'e':
1138 case 'E':
1139 case 'o':
1140 case 'O':
1141 case 'c':
1142 case 'C':
1143 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
1144 lept_stderr("*** op: %s invalid\n", op);
1145 valid = FALSE;
1146 break;
1147 }
1148 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
1149 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
1150 op, w, h);
1151 valid = FALSE;
1152 break;
1153 }
1154/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
1155 break;
1156 default:
1157 lept_stderr("*** nonexistent op = %s\n", op);
1158 valid = FALSE;
1159 }
1160 LEPT_FREE(op);
1161 }
1162 if (!valid) {
1163 sarrayDestroy(&sa);
1164 return (PIX *)ERROR_PTR("sequence invalid", __func__, NULL);
1165 }
1166
1167 /* Parse and operate */
1168 pixa = NULL;
1169 if (pdfout) {
1170 pixa = pixaCreate(0);
1171 pixaAddPix(pixa, pixs, L_CLONE);
1172 }
1173 pix1 = pixCopy(NULL, pixs);
1174 pix2 = NULL;
1175 x = 0;
1176 for (i = 0; i < nops; i++) {
1177 rawop = sarrayGetString(sa, i, L_NOCOPY);
1178 op = stringRemoveChars(rawop, " \n\t");
1179 switch (op[0])
1180 {
1181 case 'd':
1182 case 'D':
1183 sscanf(&op[1], "%d.%d", &w, &h);
1184 pix2 = pixColorMorph(pix1, L_MORPH_DILATE, w, h);
1185 pixSwapAndDestroy(&pix1, &pix2);
1186 break;
1187 case 'e':
1188 case 'E':
1189 sscanf(&op[1], "%d.%d", &w, &h);
1190 pix2 = pixColorMorph(pix1, L_MORPH_ERODE, w, h);
1191 pixSwapAndDestroy(&pix1, &pix2);
1192 break;
1193 case 'o':
1194 case 'O':
1195 sscanf(&op[1], "%d.%d", &w, &h);
1196 pix2 = pixColorMorph(pix1, L_MORPH_OPEN, w, h);
1197 pixSwapAndDestroy(&pix1, &pix2);
1198 break;
1199 case 'c':
1200 case 'C':
1201 sscanf(&op[1], "%d.%d", &w, &h);
1202 pix2 = pixColorMorph(pix1, L_MORPH_CLOSE, w, h);
1203 pixSwapAndDestroy(&pix1, &pix2);
1204 break;
1205 default:
1206 /* All invalid ops are caught in the first pass */
1207 break;
1208 }
1209 LEPT_FREE(op);
1210
1211 /* Debug output */
1212 if (dispsep > 0) {
1213 pixDisplay(pix1, x, dispy);
1214 x += dispsep;
1215 }
1216 if (pdfout)
1217 pixaAddPix(pixa, pix1, L_COPY);
1218 }
1219
1220 if (pdfout) {
1221 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1222 L_ABS(dispsep));
1223 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1224 pixaDestroy(&pixa);
1225 }
1226
1227 sarrayDestroy(&sa);
1228 return pix1;
1229}
@ L_FLATE_ENCODE
Definition imageio.h:161
PIX * pixMorphCompSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequence()
Definition morphseq.c:302
PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixColorMorphSequence()
Definition morphseq.c:1105
PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixGrayMorphSequence()
Definition morphseq.c:904
PIX * pixMorphCompSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequenceDwa()
Definition morphseq.c:596
PIX * pixMorphSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequenceDwa()
Definition morphseq.c:449
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition morphseq.c:137
l_int32 morphSequenceVerify(SARRAY *sa)
morphSequenceVerify()
Definition morphseq.c:733
@ L_COPY
Definition pix.h:505
@ L_CLONE
Definition pix.h:506
@ L_NOCOPY
Definition pix.h:503