Leptonica  1.83.1
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  *-------------------------------------------------------------------------*/
136 PIX *
138  const char *sequence,
139  l_int32 dispsep)
140 {
141 char *rawop, *op;
142 char fname[256];
143 l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
144 l_int32 level[4];
145 PIX *pix1, *pix2;
146 PIXA *pixa;
147 SARRAY *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  *-------------------------------------------------------------------------*/
301 PIX *
303  const char *sequence,
304  l_int32 dispsep)
305 {
306 char *rawop, *op;
307 char fname[256];
308 l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
309 l_int32 level[4];
310 PIX *pix1, *pix2;
311 PIXA *pixa;
312 SARRAY *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  *-------------------------------------------------------------------------*/
448 PIX *
450  const char *sequence,
451  l_int32 dispsep)
452 {
453 char *rawop, *op;
454 char fname[256];
455 l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
456 l_int32 level[4];
457 PIX *pix1, *pix2;
458 PIXA *pixa;
459 SARRAY *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  *-------------------------------------------------------------------------*/
595 PIX *
597  const char *sequence,
598  l_int32 dispsep)
599 {
600 char *rawop, *op;
601 char fname[256];
602 l_int32 nops, i, j, nred, fact, w, h, x, border, pdfout;
603 l_int32 level[4];
604 PIX *pix1, *pix2;
605 PIXA *pixa;
606 SARRAY *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  *-------------------------------------------------------------------------*/
732 l_int32
734 {
735 char *rawop, *op;
736 l_int32 nops, i, j, nred, fact, valid, w, h, netred, border;
737 l_int32 level[4];
738 l_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  *-----------------------------------------------------------------*/
903 PIX *
905  const char *sequence,
906  l_int32 dispsep,
907  l_int32 dispy)
908 {
909 char *rawop, *op;
910 char fname[256];
911 l_int32 nops, i, valid, w, h, x, pdfout;
912 PIX *pix1, *pix2;
913 PIXA *pixa;
914 SARRAY *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  *-----------------------------------------------------------------*/
1104 PIX *
1106  const char *sequence,
1107  l_int32 dispsep,
1108  l_int32 dispy)
1109 {
1110 char *rawop, *op;
1111 char fname[256];
1112 l_int32 nops, i, valid, w, h, x, pdfout;
1113 PIX *pix1, *pix2;
1114 PIXA *pixa;
1115 SARRAY *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 }
PIX * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:150
PIX * pixColorMorph(PIX *pixs, l_int32 type, l_int32 hsize, l_int32 vsize)
pixColorMorph()
Definition: colormorph.c:69
PIX * pixCloseGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseGray()
Definition: graymorph.c:520
PIX * pixDilateGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateGray()
Definition: graymorph.c:276
PIX * pixOpenGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenGray()
Definition: graymorph.c:390
PIX * pixErodeGray(PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeGray()
Definition: graymorph.c:162
@ L_FLATE_ENCODE
Definition: imageio.h:161
PIX * pixOpenBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrick()
Definition: morph.c:808
PIX * pixOpenCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrick()
Definition: morph.c:1423
PIX * pixCloseSafeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeCompBrick()
Definition: morph.c:1644
PIX * pixCloseSafeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeBrick()
Definition: morph.c:953
PIX * pixErodeBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrick()
Definition: morph.c:740
PIX * pixDilateBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrick()
Definition: morph.c:672
PIX * pixDilateCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrick()
Definition: morph.c:1214
PIX * pixErodeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrick()
Definition: morph.c:1323
PIX * pixTophat(PIX *pixs, l_int32 hsize, l_int32 vsize, l_int32 type)
pixTophat()
Definition: morphapp.c:1177
PIX * pixErodeCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeCompBrickDwa()
Definition: morphdwa.c:739
PIX * pixOpenCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenCompBrickDwa()
Definition: morphdwa.c:866
PIX * pixCloseCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseCompBrickDwa()
Definition: morphdwa.c:1031
PIX * pixOpenBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixOpenBrickDwa()
Definition: morphdwa.c:378
PIX * pixCloseBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseBrickDwa()
Definition: morphdwa.c:483
PIX * pixErodeBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixErodeBrickDwa()
Definition: morphdwa.c:278
PIX * pixDilateCompBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateCompBrickDwa()
Definition: morphdwa.c:608
PIX * pixDilateBrickDwa(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixDilateBrickDwa()
Definition: morphdwa.c:178
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
PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixColorMorphSequence()
Definition: morphseq.c:1105
PIX * pixMorphCompSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequenceDwa()
Definition: morphseq.c:596
PIX * pixGrayMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy)
pixGrayMorphSequence()
Definition: morphseq.c:904
PIX * pixMorphCompSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequence()
Definition: morphseq.c:302
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:756
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:689
l_ok pixSwapAndDestroy(PIX **ppixd, PIX **ppixs)
pixSwapAndDestroy()
Definition: pix1.c:968
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1773
PIX * pixRemoveBorder(PIX *pixs, l_int32 npix)
pixRemoveBorder()
Definition: pix2.c:1977
@ L_COPY
Definition: pix.h:505
@ L_CLONE
Definition: pix.h:506
@ L_NOCOPY
Definition: pix.h:503
l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:167
SARRAY * sarrayCreate(l_int32 n)
sarrayCreate()
Definition: sarray1.c:169
char * sarrayGetString(SARRAY *sa, l_int32 index, l_int32 copyflag)
sarrayGetString()
Definition: sarray1.c:673
l_int32 sarrayGetCount(SARRAY *sa)
sarrayGetCount()
Definition: sarray1.c:617
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:353
PIX * pixExpandReplicate(PIX *pixs, l_int32 factor)
pixExpandReplicate()
Definition: scale2.c:852
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
char * stringRemoveChars(const char *src, const char *remchars)
stringRemoveChars()
Definition: utils2.c:798