Leptonica 1.82.0
Image processing and image analysis suite
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 PROCNAME("pixMorphSequence");
150
151 if (!pixs)
152 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
153 if (!sequence)
154 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
155
156 /* Split sequence into individual operations */
157 sa = sarrayCreate(0);
158 sarraySplitString(sa, sequence, "+");
159 nops = sarrayGetCount(sa);
160 pdfout = (dispsep < 0) ? 1 : 0;
161 if (!morphSequenceVerify(sa)) {
162 sarrayDestroy(&sa);
163 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
164 }
165
166 /* Parse and operate */
167 pixa = NULL;
168 if (pdfout) {
169 pixa = pixaCreate(0);
170 pixaAddPix(pixa, pixs, L_CLONE);
171 }
172 border = 0;
173 pix1 = pixCopy(NULL, pixs);
174 pix2 = NULL;
175 x = 0;
176 for (i = 0; i < nops; i++) {
177 rawop = sarrayGetString(sa, i, L_NOCOPY);
178 op = stringRemoveChars(rawop, " \n\t");
179 switch (op[0])
180 {
181 case 'd':
182 case 'D':
183 sscanf(&op[1], "%d.%d", &w, &h);
184 pix2 = pixDilateBrick(NULL, pix1, w, h);
185 pixSwapAndDestroy(&pix1, &pix2);
186 break;
187 case 'e':
188 case 'E':
189 sscanf(&op[1], "%d.%d", &w, &h);
190 pix2 = pixErodeBrick(NULL, pix1, w, h);
191 pixSwapAndDestroy(&pix1, &pix2);
192 break;
193 case 'o':
194 case 'O':
195 sscanf(&op[1], "%d.%d", &w, &h);
196 pixOpenBrick(pix1, pix1, w, h);
197 break;
198 case 'c':
199 case 'C':
200 sscanf(&op[1], "%d.%d", &w, &h);
201 pixCloseSafeBrick(pix1, pix1, w, h);
202 break;
203 case 'r':
204 case 'R':
205 nred = strlen(op) - 1;
206 for (j = 0; j < nred; j++)
207 level[j] = op[j + 1] - '0';
208 for (j = nred; j < 4; j++)
209 level[j] = 0;
210 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
211 level[2], level[3]);
212 pixSwapAndDestroy(&pix1, &pix2);
213 break;
214 case 'x':
215 case 'X':
216 sscanf(&op[1], "%d", &fact);
217 pix2 = pixExpandReplicate(pix1, fact);
218 pixSwapAndDestroy(&pix1, &pix2);
219 break;
220 case 'b':
221 case 'B':
222 sscanf(&op[1], "%d", &border);
223 pix2 = pixAddBorder(pix1, border, 0);
224 pixSwapAndDestroy(&pix1, &pix2);
225 break;
226 default:
227 /* All invalid ops are caught in the first pass */
228 break;
229 }
230 LEPT_FREE(op);
231
232 /* Debug output */
233 if (dispsep > 0) {
234 pixDisplay(pix1, x, 0);
235 x += dispsep;
236 }
237 if (pdfout)
238 pixaAddPix(pixa, pix1, L_COPY);
239 }
240 if (border > 0) {
241 pix2 = pixRemoveBorder(pix1, border);
242 pixSwapAndDestroy(&pix1, &pix2);
243 }
244
245 if (pdfout) {
246 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
247 L_ABS(dispsep));
248 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
249 pixaDestroy(&pixa);
250 }
251
252 sarrayDestroy(&sa);
253 return pix1;
254}
255
256
257/*-------------------------------------------------------------------------*
258 * Run a sequence of binary composite rasterop morphological operations *
259 *-------------------------------------------------------------------------*/
303PIX *
305 const char *sequence,
306 l_int32 dispsep)
307{
308char *rawop, *op;
309char fname[256];
310l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
311l_int32 level[4];
312PIX *pix1, *pix2;
313PIXA *pixa;
314SARRAY *sa;
315
316 PROCNAME("pixMorphCompSequence");
317
318 if (!pixs)
319 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
320 if (!sequence)
321 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
322
323 /* Split sequence into individual operations */
324 sa = sarrayCreate(0);
325 sarraySplitString(sa, sequence, "+");
326 nops = sarrayGetCount(sa);
327 pdfout = (dispsep < 0) ? 1 : 0;
328
329 if (!morphSequenceVerify(sa)) {
330 sarrayDestroy(&sa);
331 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
332 }
333
334 /* Parse and operate */
335 pixa = NULL;
336 if (pdfout) {
337 pixa = pixaCreate(0);
338 pixaAddPix(pixa, pixs, L_CLONE);
339 }
340 border = 0;
341 pix1 = pixCopy(NULL, pixs);
342 pix2 = NULL;
343 x = 0;
344 for (i = 0; i < nops; i++) {
345 rawop = sarrayGetString(sa, i, L_NOCOPY);
346 op = stringRemoveChars(rawop, " \n\t");
347 switch (op[0])
348 {
349 case 'd':
350 case 'D':
351 sscanf(&op[1], "%d.%d", &w, &h);
352 pix2 = pixDilateCompBrick(NULL, pix1, w, h);
353 pixSwapAndDestroy(&pix1, &pix2);
354 break;
355 case 'e':
356 case 'E':
357 sscanf(&op[1], "%d.%d", &w, &h);
358 pix2 = pixErodeCompBrick(NULL, pix1, w, h);
359 pixSwapAndDestroy(&pix1, &pix2);
360 break;
361 case 'o':
362 case 'O':
363 sscanf(&op[1], "%d.%d", &w, &h);
364 pixOpenCompBrick(pix1, pix1, w, h);
365 break;
366 case 'c':
367 case 'C':
368 sscanf(&op[1], "%d.%d", &w, &h);
369 pixCloseSafeCompBrick(pix1, pix1, w, h);
370 break;
371 case 'r':
372 case 'R':
373 nred = strlen(op) - 1;
374 for (j = 0; j < nred; j++)
375 level[j] = op[j + 1] - '0';
376 for (j = nred; j < 4; j++)
377 level[j] = 0;
378 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
379 level[2], level[3]);
380 pixSwapAndDestroy(&pix1, &pix2);
381 break;
382 case 'x':
383 case 'X':
384 sscanf(&op[1], "%d", &fact);
385 pix2 = pixExpandReplicate(pix1, fact);
386 pixSwapAndDestroy(&pix1, &pix2);
387 break;
388 case 'b':
389 case 'B':
390 sscanf(&op[1], "%d", &border);
391 pix2 = pixAddBorder(pix1, border, 0);
392 pixSwapAndDestroy(&pix1, &pix2);
393 break;
394 default:
395 /* All invalid ops are caught in the first pass */
396 break;
397 }
398 LEPT_FREE(op);
399
400 /* Debug output */
401 if (dispsep > 0) {
402 pixDisplay(pix1, x, 0);
403 x += dispsep;
404 }
405 if (pdfout)
406 pixaAddPix(pixa, pix1, L_COPY);
407 }
408 if (border > 0) {
409 pix2 = pixRemoveBorder(pix1, border);
410 pixSwapAndDestroy(&pix1, &pix2);
411 }
412
413 if (pdfout) {
414 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
415 L_ABS(dispsep));
416 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
417 pixaDestroy(&pixa);
418 }
419
420 sarrayDestroy(&sa);
421 return pix1;
422}
423
424
425/*-------------------------------------------------------------------------*
426 * Run a sequence of binary dwa morphological operations *
427 *-------------------------------------------------------------------------*/
452PIX *
454 const char *sequence,
455 l_int32 dispsep)
456{
457char *rawop, *op;
458char fname[256];
459l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
460l_int32 level[4];
461PIX *pix1, *pix2;
462PIXA *pixa;
463SARRAY *sa;
464
465 PROCNAME("pixMorphSequenceDwa");
466
467 if (!pixs)
468 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
469 if (!sequence)
470 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
471
472 /* Split sequence into individual operations */
473 sa = sarrayCreate(0);
474 sarraySplitString(sa, sequence, "+");
475 nops = sarrayGetCount(sa);
476 pdfout = (dispsep < 0) ? 1 : 0;
477
478 if (!morphSequenceVerify(sa)) {
479 sarrayDestroy(&sa);
480 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
481 }
482
483 /* Parse and operate */
484 pixa = NULL;
485 if (pdfout) {
486 pixa = pixaCreate(0);
487 pixaAddPix(pixa, pixs, L_CLONE);
488 }
489 border = 0;
490 pix1 = pixCopy(NULL, pixs);
491 pix2 = NULL;
492 x = 0;
493 for (i = 0; i < nops; i++) {
494 rawop = sarrayGetString(sa, i, L_NOCOPY);
495 op = stringRemoveChars(rawop, " \n\t");
496 switch (op[0])
497 {
498 case 'd':
499 case 'D':
500 sscanf(&op[1], "%d.%d", &w, &h);
501 pix2 = pixDilateBrickDwa(NULL, pix1, w, h);
502 pixSwapAndDestroy(&pix1, &pix2);
503 break;
504 case 'e':
505 case 'E':
506 sscanf(&op[1], "%d.%d", &w, &h);
507 pix2 = pixErodeBrickDwa(NULL, pix1, w, h);
508 pixSwapAndDestroy(&pix1, &pix2);
509 break;
510 case 'o':
511 case 'O':
512 sscanf(&op[1], "%d.%d", &w, &h);
513 pixOpenBrickDwa(pix1, pix1, w, h);
514 break;
515 case 'c':
516 case 'C':
517 sscanf(&op[1], "%d.%d", &w, &h);
518 pixCloseBrickDwa(pix1, pix1, w, h);
519 break;
520 case 'r':
521 case 'R':
522 nred = strlen(op) - 1;
523 for (j = 0; j < nred; j++)
524 level[j] = op[j + 1] - '0';
525 for (j = nred; j < 4; j++)
526 level[j] = 0;
527 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
528 level[2], level[3]);
529 pixSwapAndDestroy(&pix1, &pix2);
530 break;
531 case 'x':
532 case 'X':
533 sscanf(&op[1], "%d", &fact);
534 pix2 = pixExpandReplicate(pix1, fact);
535 pixSwapAndDestroy(&pix1, &pix2);
536 break;
537 case 'b':
538 case 'B':
539 sscanf(&op[1], "%d", &border);
540 pix2 = pixAddBorder(pix1, border, 0);
541 pixSwapAndDestroy(&pix1, &pix2);
542 break;
543 default:
544 /* All invalid ops are caught in the first pass */
545 break;
546 }
547 LEPT_FREE(op);
548
549 /* Debug output */
550 if (dispsep > 0) {
551 pixDisplay(pix1, x, 0);
552 x += dispsep;
553 }
554 if (pdfout)
555 pixaAddPix(pixa, pix1, L_COPY);
556 }
557 if (border > 0) {
558 pix2 = pixRemoveBorder(pix1, border);
559 pixSwapAndDestroy(&pix1, &pix2);
560 }
561
562 if (pdfout) {
563 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
564 L_ABS(dispsep));
565 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
566 pixaDestroy(&pixa);
567 }
568
569 sarrayDestroy(&sa);
570 return pix1;
571}
572
573
574/*-------------------------------------------------------------------------*
575 * Run a sequence of binary composite dwa morphological operations *
576 *-------------------------------------------------------------------------*/
601PIX *
603 const char *sequence,
604 l_int32 dispsep)
605{
606char *rawop, *op;
607char fname[256];
608l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
609l_int32 level[4];
610PIX *pix1, *pix2;
611PIXA *pixa;
612SARRAY *sa;
613
614 PROCNAME("pixMorphCompSequenceDwa");
615
616 if (!pixs)
617 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
618 if (!sequence)
619 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
620
621 /* Split sequence into individual operations */
622 sa = sarrayCreate(0);
623 sarraySplitString(sa, sequence, "+");
624 nops = sarrayGetCount(sa);
625 pdfout = (dispsep < 0) ? 1 : 0;
626
627 if (!morphSequenceVerify(sa)) {
628 sarrayDestroy(&sa);
629 return (PIX *)ERROR_PTR("sequence not valid", procName, NULL);
630 }
631
632 /* Parse and operate */
633 pixa = NULL;
634 if (pdfout) {
635 pixa = pixaCreate(0);
636 pixaAddPix(pixa, pixs, L_CLONE);
637 }
638 border = 0;
639 pix1 = pixCopy(NULL, pixs);
640 pix2 = NULL;
641 x = 0;
642 for (i = 0; i < nops; i++) {
643 rawop = sarrayGetString(sa, i, L_NOCOPY);
644 op = stringRemoveChars(rawop, " \n\t");
645 switch (op[0])
646 {
647 case 'd':
648 case 'D':
649 sscanf(&op[1], "%d.%d", &w, &h);
650 pix2 = pixDilateCompBrickDwa(NULL, pix1, w, h);
651 pixSwapAndDestroy(&pix1, &pix2);
652 break;
653 case 'e':
654 case 'E':
655 sscanf(&op[1], "%d.%d", &w, &h);
656 pix2 = pixErodeCompBrickDwa(NULL, pix1, w, h);
657 pixSwapAndDestroy(&pix1, &pix2);
658 break;
659 case 'o':
660 case 'O':
661 sscanf(&op[1], "%d.%d", &w, &h);
662 pixOpenCompBrickDwa(pix1, pix1, w, h);
663 break;
664 case 'c':
665 case 'C':
666 sscanf(&op[1], "%d.%d", &w, &h);
667 pixCloseCompBrickDwa(pix1, pix1, w, h);
668 break;
669 case 'r':
670 case 'R':
671 nred = strlen(op) - 1;
672 for (j = 0; j < nred; j++)
673 level[j] = op[j + 1] - '0';
674 for (j = nred; j < 4; j++)
675 level[j] = 0;
676 pix2 = pixReduceRankBinaryCascade(pix1, level[0], level[1],
677 level[2], level[3]);
678 pixSwapAndDestroy(&pix1, &pix2);
679 break;
680 case 'x':
681 case 'X':
682 sscanf(&op[1], "%d", &fact);
683 pix2 = pixExpandReplicate(pix1, fact);
684 pixSwapAndDestroy(&pix1, &pix2);
685 break;
686 case 'b':
687 case 'B':
688 sscanf(&op[1], "%d", &border);
689 pix2 = pixAddBorder(pix1, border, 0);
690 pixSwapAndDestroy(&pix1, &pix2);
691 break;
692 default:
693 /* All invalid ops are caught in the first pass */
694 break;
695 }
696 LEPT_FREE(op);
697
698 /* Debug output */
699 if (dispsep > 0) {
700 pixDisplay(pix1, x, 0);
701 x += dispsep;
702 }
703 if (pdfout)
704 pixaAddPix(pixa, pix1, L_COPY);
705 }
706 if (border > 0) {
707 pix2 = pixRemoveBorder(pix1, border);
708 pixSwapAndDestroy(&pix1, &pix2);
709 }
710
711 if (pdfout) {
712 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
713 L_ABS(dispsep));
714 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
715 pixaDestroy(&pixa);
716 }
717
718 sarrayDestroy(&sa);
719 return pix1;
720}
721
722
723/*-------------------------------------------------------------------------*
724 * Parser verifier for binary morphological operations *
725 *-------------------------------------------------------------------------*/
740l_int32
742{
743char *rawop, *op;
744l_int32 nops, i, j, nred, fact, valid, w, h, netred, border;
745l_int32 level[4];
746l_int32 intlogbase2[5] = {1, 2, 3, 0, 4}; /* of arg/4 */
747
748 PROCNAME("morphSequenceVerify");
749
750 if (!sa)
751 return ERROR_INT("sa not defined", procName, FALSE);
752
753 nops = sarrayGetCount(sa);
754 valid = TRUE;
755 netred = 0;
756 border = 0;
757 for (i = 0; i < nops; i++) {
758 rawop = sarrayGetString(sa, i, L_NOCOPY);
759 op = stringRemoveChars(rawop, " \n\t");
760 switch (op[0])
761 {
762 case 'd':
763 case 'D':
764 case 'e':
765 case 'E':
766 case 'o':
767 case 'O':
768 case 'c':
769 case 'C':
770 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
771 lept_stderr("*** op: %s invalid\n", op);
772 valid = FALSE;
773 break;
774 }
775 if (w <= 0 || h <= 0) {
776 lept_stderr("*** op: %s; w = %d, h = %d; must both be > 0\n",
777 op, w, h);
778 valid = FALSE;
779 break;
780 }
781/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
782 break;
783 case 'r':
784 case 'R':
785 nred = strlen(op) - 1;
786 netred += nred;
787 if (nred < 1 || nred > 4) {
789 "*** op = %s; num reduct = %d; must be in {1,2,3,4}\n",
790 op, nred);
791 valid = FALSE;
792 break;
793 }
794 for (j = 0; j < nred; j++) {
795 level[j] = op[j + 1] - '0';
796 if (level[j] < 1 || level[j] > 4) {
797 lept_stderr("*** op = %s; level[%d] = %d is invalid\n",
798 op, j, level[j]);
799 valid = FALSE;
800 break;
801 }
802 }
803 if (!valid)
804 break;
805/* lept_stderr("op = %s", op); */
806 for (j = 0; j < nred; j++) {
807 level[j] = op[j + 1] - '0';
808/* lept_stderr(", level[%d] = %d", j, level[j]); */
809 }
810/* lept_stderr("\n"); */
811 break;
812 case 'x':
813 case 'X':
814 if (sscanf(&op[1], "%d", &fact) != 1) {
815 lept_stderr("*** op: %s; fact invalid\n", op);
816 valid = FALSE;
817 break;
818 }
819 if (fact != 2 && fact != 4 && fact != 8 && fact != 16) {
820 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
821 valid = FALSE;
822 break;
823 }
824 netred -= intlogbase2[fact / 4];
825/* lept_stderr("op = %s; fact = %d\n", op, fact); */
826 break;
827 case 'b':
828 case 'B':
829 if (sscanf(&op[1], "%d", &fact) != 1) {
830 lept_stderr("*** op: %s; fact invalid\n", op);
831 valid = FALSE;
832 break;
833 }
834 if (i > 0) {
835 lept_stderr("*** op = %s; must be first op\n", op);
836 valid = FALSE;
837 break;
838 }
839 if (fact < 1) {
840 lept_stderr("*** op = %s; invalid fact = %d\n", op, fact);
841 valid = FALSE;
842 break;
843 }
844 border = fact;
845/* lept_stderr("op = %s; fact = %d\n", op, fact); */
846 break;
847 default:
848 lept_stderr("*** nonexistent op = %s\n", op);
849 valid = FALSE;
850 }
851 LEPT_FREE(op);
852 }
853
854 if (border != 0 && netred != 0) {
855 lept_stderr("*** op = %s; border added but net reduction not 0\n", op);
856 valid = FALSE;
857 }
858 return valid;
859}
860
861
862/*-----------------------------------------------------------------*
863 * Run a sequence of grayscale morphological operations *
864 *-----------------------------------------------------------------*/
913PIX *
915 const char *sequence,
916 l_int32 dispsep,
917 l_int32 dispy)
918{
919char *rawop, *op;
920char fname[256];
921l_int32 nops, i, valid, w, h, x, pdfout;
922PIX *pix1, *pix2;
923PIXA *pixa;
924SARRAY *sa;
925
926 PROCNAME("pixGrayMorphSequence");
927
928 if (!pixs)
929 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
930 if (!sequence)
931 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
932
933 /* Split sequence into individual operations */
934 sa = sarrayCreate(0);
935 sarraySplitString(sa, sequence, "+");
936 nops = sarrayGetCount(sa);
937 pdfout = (dispsep < 0) ? 1 : 0;
938
939 /* Verify that the operation sequence is valid */
940 valid = TRUE;
941 for (i = 0; i < nops; i++) {
942 rawop = sarrayGetString(sa, i, L_NOCOPY);
943 op = stringRemoveChars(rawop, " \n\t");
944 switch (op[0])
945 {
946 case 'd':
947 case 'D':
948 case 'e':
949 case 'E':
950 case 'o':
951 case 'O':
952 case 'c':
953 case 'C':
954 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
955 lept_stderr("*** op: %s invalid\n", op);
956 valid = FALSE;
957 break;
958 }
959 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
960 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
961 op, w, h);
962 valid = FALSE;
963 break;
964 }
965/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
966 break;
967 case 't':
968 case 'T':
969 if (op[1] != 'w' && op[1] != 'W' &&
970 op[1] != 'b' && op[1] != 'B') {
972 "*** op = %s; arg %c must be 'w' or 'b'\n", op, op[1]);
973 valid = FALSE;
974 break;
975 }
976 sscanf(&op[2], "%d.%d", &w, &h);
977 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
978 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
979 op, w, h);
980 valid = FALSE;
981 break;
982 }
983/* lept_stderr("op = %s", op); */
984 break;
985 default:
986 lept_stderr("*** nonexistent op = %s\n", op);
987 valid = FALSE;
988 }
989 LEPT_FREE(op);
990 }
991 if (!valid) {
992 sarrayDestroy(&sa);
993 return (PIX *)ERROR_PTR("sequence invalid", procName, NULL);
994 }
995
996 /* Parse and operate */
997 pixa = NULL;
998 if (pdfout) {
999 pixa = pixaCreate(0);
1000 pixaAddPix(pixa, pixs, L_CLONE);
1001 }
1002 pix1 = pixCopy(NULL, pixs);
1003 pix2 = NULL;
1004 x = 0;
1005 for (i = 0; i < nops; i++) {
1006 rawop = sarrayGetString(sa, i, L_NOCOPY);
1007 op = stringRemoveChars(rawop, " \n\t");
1008 switch (op[0])
1009 {
1010 case 'd':
1011 case 'D':
1012 sscanf(&op[1], "%d.%d", &w, &h);
1013 pix2 = pixDilateGray(pix1, w, h);
1014 pixSwapAndDestroy(&pix1, &pix2);
1015 break;
1016 case 'e':
1017 case 'E':
1018 sscanf(&op[1], "%d.%d", &w, &h);
1019 pix2 = pixErodeGray(pix1, w, h);
1020 pixSwapAndDestroy(&pix1, &pix2);
1021 break;
1022 case 'o':
1023 case 'O':
1024 sscanf(&op[1], "%d.%d", &w, &h);
1025 pix2 = pixOpenGray(pix1, w, h);
1026 pixSwapAndDestroy(&pix1, &pix2);
1027 break;
1028 case 'c':
1029 case 'C':
1030 sscanf(&op[1], "%d.%d", &w, &h);
1031 pix2 = pixCloseGray(pix1, w, h);
1032 pixSwapAndDestroy(&pix1, &pix2);
1033 break;
1034 case 't':
1035 case 'T':
1036 sscanf(&op[2], "%d.%d", &w, &h);
1037 if (op[1] == 'w' || op[1] == 'W')
1038 pix2 = pixTophat(pix1, w, h, L_TOPHAT_WHITE);
1039 else /* 'b' or 'B' */
1040 pix2 = pixTophat(pix1, w, h, L_TOPHAT_BLACK);
1041 pixSwapAndDestroy(&pix1, &pix2);
1042 break;
1043 default:
1044 /* All invalid ops are caught in the first pass */
1045 break;
1046 }
1047 LEPT_FREE(op);
1048
1049 /* Debug output */
1050 if (dispsep > 0) {
1051 pixDisplay(pix1, x, dispy);
1052 x += dispsep;
1053 }
1054 if (pdfout)
1055 pixaAddPix(pixa, pix1, L_COPY);
1056 }
1057
1058 if (pdfout) {
1059 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1060 L_ABS(dispsep));
1061 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1062 pixaDestroy(&pixa);
1063 }
1064
1065 sarrayDestroy(&sa);
1066 return pix1;
1067}
1068
1069
1070/*-----------------------------------------------------------------*
1071 * Run a sequence of color morphological operations *
1072 *-----------------------------------------------------------------*/
1116PIX *
1118 const char *sequence,
1119 l_int32 dispsep,
1120 l_int32 dispy)
1121{
1122char *rawop, *op;
1123char fname[256];
1124l_int32 nops, i, valid, w, h, x, pdfout;
1125PIX *pix1, *pix2;
1126PIXA *pixa;
1127SARRAY *sa;
1128
1129 PROCNAME("pixColorMorphSequence");
1130
1131 if (!pixs)
1132 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1133 if (!sequence)
1134 return (PIX *)ERROR_PTR("sequence not defined", procName, NULL);
1135
1136 /* Split sequence into individual operations */
1137 sa = sarrayCreate(0);
1138 sarraySplitString(sa, sequence, "+");
1139 nops = sarrayGetCount(sa);
1140 pdfout = (dispsep < 0) ? 1 : 0;
1141
1142 /* Verify that the operation sequence is valid */
1143 valid = TRUE;
1144 for (i = 0; i < nops; i++) {
1145 rawop = sarrayGetString(sa, i, L_NOCOPY);
1146 op = stringRemoveChars(rawop, " \n\t");
1147 switch (op[0])
1148 {
1149 case 'd':
1150 case 'D':
1151 case 'e':
1152 case 'E':
1153 case 'o':
1154 case 'O':
1155 case 'c':
1156 case 'C':
1157 if (sscanf(&op[1], "%d.%d", &w, &h) != 2) {
1158 lept_stderr("*** op: %s invalid\n", op);
1159 valid = FALSE;
1160 break;
1161 }
1162 if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) {
1163 lept_stderr("*** op: %s; w = %d, h = %d; must both be odd\n",
1164 op, w, h);
1165 valid = FALSE;
1166 break;
1167 }
1168/* lept_stderr("op = %s; w = %d, h = %d\n", op, w, h); */
1169 break;
1170 default:
1171 lept_stderr("*** nonexistent op = %s\n", op);
1172 valid = FALSE;
1173 }
1174 LEPT_FREE(op);
1175 }
1176 if (!valid) {
1177 sarrayDestroy(&sa);
1178 return (PIX *)ERROR_PTR("sequence invalid", procName, NULL);
1179 }
1180
1181 /* Parse and operate */
1182 pixa = NULL;
1183 if (pdfout) {
1184 pixa = pixaCreate(0);
1185 pixaAddPix(pixa, pixs, L_CLONE);
1186 }
1187 pix1 = pixCopy(NULL, pixs);
1188 pix2 = NULL;
1189 x = 0;
1190 for (i = 0; i < nops; i++) {
1191 rawop = sarrayGetString(sa, i, L_NOCOPY);
1192 op = stringRemoveChars(rawop, " \n\t");
1193 switch (op[0])
1194 {
1195 case 'd':
1196 case 'D':
1197 sscanf(&op[1], "%d.%d", &w, &h);
1198 pix2 = pixColorMorph(pix1, L_MORPH_DILATE, w, h);
1199 pixSwapAndDestroy(&pix1, &pix2);
1200 break;
1201 case 'e':
1202 case 'E':
1203 sscanf(&op[1], "%d.%d", &w, &h);
1204 pix2 = pixColorMorph(pix1, L_MORPH_ERODE, w, h);
1205 pixSwapAndDestroy(&pix1, &pix2);
1206 break;
1207 case 'o':
1208 case 'O':
1209 sscanf(&op[1], "%d.%d", &w, &h);
1210 pix2 = pixColorMorph(pix1, L_MORPH_OPEN, w, h);
1211 pixSwapAndDestroy(&pix1, &pix2);
1212 break;
1213 case 'c':
1214 case 'C':
1215 sscanf(&op[1], "%d.%d", &w, &h);
1216 pix2 = pixColorMorph(pix1, L_MORPH_CLOSE, w, h);
1217 pixSwapAndDestroy(&pix1, &pix2);
1218 break;
1219 default:
1220 /* All invalid ops are caught in the first pass */
1221 break;
1222 }
1223 LEPT_FREE(op);
1224
1225 /* Debug output */
1226 if (dispsep > 0) {
1227 pixDisplay(pix1, x, dispy);
1228 x += dispsep;
1229 }
1230 if (pdfout)
1231 pixaAddPix(pixa, pix1, L_COPY);
1232 }
1233
1234 if (pdfout) {
1235 snprintf(fname, sizeof(fname), "/tmp/lept/seq_output_%d.pdf",
1236 L_ABS(dispsep));
1237 pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname);
1238 pixaDestroy(&pixa);
1239 }
1240
1241 sarrayDestroy(&sa);
1242 return pix1;
1243}
PIX * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:152
PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize)
pixColorMorph()
Definition: colormorph.c:69
PIX * pixDilateGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateGray()
Definition: graymorph.c:278
PIX * pixErodeGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeGray()
Definition: graymorph.c:162
PIX * pixCloseGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseGray()
Definition: graymorph.c:526
PIX * pixOpenGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenGray()
Definition: graymorph.c:394
@ L_FLATE_ENCODE
Definition: imageio.h:161
PIX * pixErodeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrick()
Definition: morph.c:1355
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:688
PIX * pixDilateCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrick()
Definition: morph.c:1244
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition: morph.c:977
PIX * pixOpenBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrick()
Definition: morph.c:828
PIX * pixOpenCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrick()
Definition: morph.c:1457
PIX * pixCloseSafeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeCompBrick()
Definition: morph.c:1682
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition: morph.c:758
PIX * pixTophat(PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 type)
pixTophat()
Definition: morphapp.c:1206
PIX * pixDilateCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrickDwa()
Definition: morphdwa.c:616
PIX * pixCloseBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrickDwa()
Definition: morphdwa.c:489
PIX * pixDilateBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrickDwa()
Definition: morphdwa.c:178
PIX * pixErodeBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrickDwa()
Definition: morphdwa.c:280
PIX * pixErodeCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrickDwa()
Definition: morphdwa.c:749
PIX * pixOpenBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrickDwa()
Definition: morphdwa.c:382
PIX * pixCloseCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseCompBrickDwa()
Definition: morphdwa.c:1045
PIX * pixOpenCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrickDwa()
Definition: morphdwa.c:878
PIX * pixMorphCompSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequence()
Definition: morphseq.c:304
PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixColorMorphSequence()
Definition: morphseq.c:1117
PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixGrayMorphSequence()
Definition: morphseq.c:914
PIX * pixMorphCompSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequenceDwa()
Definition: morphseq.c:602
PIX * pixMorphSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequenceDwa()
Definition: morphseq.c:453
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:137
l_int32 morphSequenceVerify(SARRAY *sa)
morphSequenceVerify()
Definition: morphseq.c:741
l_ok pixaConvertToPdf(PIXA *pixa, l_int32 res, l_float32 scalefactor, l_int32 type, l_int32 quality, const char *title, const char *fileout)
pixaConvertToPdf()
Definition: pdfio1.c:790
l_ok pixSwapAndDestroy(PIX **ppixd, PIX **ppixs)
pixSwapAndDestroy()
Definition: pix1.c:993
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1972
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1823
@ L_COPY
Definition: pix.h:712
@ L_CLONE
Definition: pix.h:713
@ L_NOCOPY
Definition: pix.h:710
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:506
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:412
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
SARRAY * sarrayCreate(l_int32 n)
sarrayCreate()
Definition: sarray1.c:170
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:703
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:643
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:362
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:872
Definition: pix.h:139
Definition: pix.h:456
Definition: array.h:127
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
char * stringRemoveChars(const char *src, const char *remchars)
stringRemoveChars()
Definition: utils2.c:822