main(int argc, char **argv) { char *filein; char buf[L_BUF_SIZE]; l_int32 size; PIX *pixs, *pixt1, *pixt2; static char mainName[] = "colormorphtest"; if (argc != 3) exit(ERROR_INT(" Syntax: colormorphtest filein size", mainName, 1)); filein = argv[1]; size = atoi(argv[2]); if (size % 2 == 0) size++; if ((pixs = pixRead(filein)) == NULL) exit(ERROR_INT("pixs not read", mainName, 1)); pixt1 = pixColorMorph(pixs, L_MORPH_DILATE, size, size); sprintf(buf, "d%d.%d", size, size); pixt2 = pixColorMorphSequence(pixs, buf, 0, 0); pixCompare(pixt1, pixt2, "Correct for dilation", "Error on dilation"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixColorMorph(pixs, L_MORPH_ERODE, size, size); sprintf(buf, "e%d.%d", size, size); pixt2 = pixColorMorphSequence(pixs, buf, 0, 0); pixCompare(pixt1, pixt2, "Correct for erosion", "Error on erosion"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixColorMorph(pixs, L_MORPH_OPEN, size, size); sprintf(buf, "o%d.%d", size, size); pixt2 = pixColorMorphSequence(pixs, buf, 0, 0); pixCompare(pixt1, pixt2, "Correct for opening", "Error on opening"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixColorMorph(pixs, L_MORPH_CLOSE, size, size); sprintf(buf, "c%d.%d", size, size); pixt2 = pixColorMorphSequence(pixs, buf, 0, 0); pixCompare(pixt1, pixt2, "Correct for closing", "Error on closing"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDisplayMultiple("/tmp/junk_write_display*"); pixDestroy(&pixs); return 0; }
/*! * pixColorMorphSequence() * * Input: pixs * sequence (string specifying sequence) * dispsep (horizontal separation in pixels between * successive displays; use zero to suppress display) * dispy (if dispsep != 0, this gives the y-value of the * UL corner for display; otherwise it is ignored) * Return: pixd, or null on error * * Notes: * (1) This works on 32 bpp rgb images. * (2) Each component is processed separately. * (3) This runs a pipeline of operations; no branching is allowed. * (4) This only uses brick SELs. * (5) A new image is always produced; the input image is not changed. * (6) This contains an interpreter, allowing sequences to be * generated and run. * (7) Sel sizes (width, height) must each be odd numbers. * (8) The format of the sequence string is defined below. * (9) Intermediate results can optionally be displayed. * (10) The sequence string is formatted as follows: * - An arbitrary number of operations, each separated * by a '+' character. White space is ignored. * - Each operation begins with a case-independent character * specifying the operation: * d or D (dilation) * e or E (erosion) * o or O (opening) * c or C (closing) * - The args to the morphological operations are bricks of hits, * and are formatted as a.b, where a and b are horizontal and * vertical dimensions, rsp. (each must be an odd number) * Example valid sequences are: * "c5.3 + o7.5" * "D9.1" */ PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy) { char *rawop, *op; l_int32 nops, i, valid, w, h, x; PIX *pixt1, *pixt2; SARRAY *sa; PROCNAME("pixColorMorphSequence"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!sequence) return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */ sa = sarrayCreate(0); sarraySplitString(sa, sequence, "+"); nops = sarrayGetCount(sa); /* Verify that the operation sequence is valid */ valid = TRUE; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, 0); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': case 'e': case 'E': case 'o': case 'O': case 'c': case 'C': if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { fprintf(stderr, "*** op: %s invalid\n", op); valid = FALSE; break; } if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ break; default: fprintf(stderr, "*** nonexistent op = %s\n", op); valid = FALSE; } FREE(op); } if (!valid) { sarrayDestroy(&sa); return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); } /* Parse and operate */ pixt1 = pixCopy(NULL, pixs); pixt2 = NULL; x = 0; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, 0); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_DILATE, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'e': case 'E': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_ERODE, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'o': case 'O': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_OPEN, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; case 'c': case 'C': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_CLOSE, w, h); pixDestroy(&pixt1); pixt1 = pixClone(pixt2); pixDestroy(&pixt2); if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } break; default: /* All invalid ops are caught in the first pass */ break; } FREE(op); } sarrayDestroy(&sa); return pixt1; }
main(int argc, char **argv) { l_int32 i, wf, hf, w, h, d, same; l_float32 rank, time; PIX *pixs, *pixd, *pixt1, *pixt2, *pixt3, *pixt4; PIXA *pixa; char *filein, *fileout; static char mainName[] = "ranktest"; if (argc != 6) exit(ERROR_INT(" Syntax: ranktest filein wf hf rank fileout", mainName, 1)); filein = argv[1]; wf = atoi(argv[2]); hf = atoi(argv[3]); rank = atof(argv[4]); fileout = argv[5]; if ((pixs = pixRead(filein)) == NULL) exit(ERROR_INT("pix not made", mainName, 1)); pixGetDimensions(pixs, &w, &h, &d); if (d != 8 && d != 32) exit(ERROR_INT("pix neither 8 nor 32 bpp", mainName, 1)); startTimer(); pixd = pixRankFilter(pixs, wf, hf, rank); time = stopTimer(); fprintf(stderr, "Time = %7.3f sec\n", time); fprintf(stderr, "MPix/sec: %7.3f\n", 0.000001 * w * h / time); pixDisplay(pixs, 0, 0); pixDisplay(pixd, 600, 0); pixWrite(fileout, pixd, IFF_PNG); pixDestroy(&pixd); /* Get results for different rank values */ for (i = 0; i <= 10; i++) { pixd = pixRankFilter(pixs, wf, hf, 0.1 * i); pixDisplayWrite(pixd, 1); pixDestroy(&pixd); } /* Make the dimensions odd to compare with dilation & erosion */ if (wf % 2 == 0) wf++; if (hf % 2 == 0) hf++; /* Get results for dilation and erosion */ if (d == 8) { pixt1 = pixDilateGray(pixs, wf, hf); pixt2 = pixErodeGray(pixs, wf, hf); } else { pixt1 = pixColorMorph(pixs, L_MORPH_DILATE, wf, hf); pixt2 = pixColorMorph(pixs, L_MORPH_ERODE, wf, hf); } pixDisplayWrite(pixt1, 1); /* dilation */ /* Get results using the rank filter for rank = 0.0 and 1.0. * Don't use 0.0 or 1.0, because those are dispatched * automatically to erosion and dilation! */ pixt3 = pixRankFilter(pixs, wf, hf, 0.0001); pixt4 = pixRankFilter(pixs, wf, hf, 0.9999); /* Compare */ pixEqual(pixt1, pixt4, &same); if (same) fprintf(stderr, "Correct: dilation results same as rank 1.0\n"); else fprintf(stderr, "Error: dilation results differ from rank 1.0\n"); pixEqual(pixt2, pixt3, &same); if (same) fprintf(stderr, "Correct: erosion results same as rank 0.0\n"); else fprintf(stderr, "Error: erosion results differ from rank 0.0\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); /* Display tiled */ pixa = pixaReadFiles("/tmp", "junk_write_display"); pixd = pixaDisplayTiledAndScaled(pixa, d, 400, 3, 0, 25, 2); pixWrite("/tmp/junktiles.jpg", pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); return 0; }
/*! * \brief pixColorMorphSequence() * * \param[in] pixs * \param[in] sequence string specifying sequence * \param[in] dispsep controls debug display of each result in the sequence: * 0: no output * > 0: gives horizontal separation in pixels between * successive displays * < 0: pdf output; abs(dispsep) is used for naming * \param[in] dispy if dispsep > 0, this gives the y-value of the * UL corner for display; otherwise it is ignored * \return pixd, or NULL on error * * <pre> * Notes: * (1) This works on 32 bpp rgb images. * (2) Each component is processed separately. * (3) This runs a pipeline of operations; no branching is allowed. * (4) This only uses brick SELs. * (5) A new image is always produced; the input image is not changed. * (6) This contains an interpreter, allowing sequences to be * generated and run. * (7) Sel sizes (width, height) must each be odd numbers. * (8) The format of the sequence string is defined below. * (9) Intermediate results can optionally be displayed. * (10) The sequence string is formatted as follows: * ~ An arbitrary number of operations, each separated * by a '+' character. White space is ignored. * ~ Each operation begins with a case-independent character * specifying the operation: * d or D (dilation) * e or E (erosion) * o or O (opening) * c or C (closing) * ~ The args to the morphological operations are bricks of hits, * and are formatted as a.b, where a and b are horizontal and * vertical dimensions, rsp. (each must be an odd number) * Example valid sequences are: * "c5.3 + o7.5" * "D9.1" * </pre> */ PIX * pixColorMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep, l_int32 dispy) { char *rawop, *op, *fname; char buf[256]; l_int32 nops, i, valid, w, h, x, pdfout; PIX *pixt1, *pixt2; PIXA *pixa; SARRAY *sa; PROCNAME("pixColorMorphSequence"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (!sequence) return (PIX *)ERROR_PTR("sequence not defined", procName, NULL); /* Split sequence into individual operations */ sa = sarrayCreate(0); sarraySplitString(sa, sequence, "+"); nops = sarrayGetCount(sa); pdfout = (dispsep < 0) ? 1 : 0; /* Verify that the operation sequence is valid */ valid = TRUE; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, L_NOCOPY); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': case 'e': case 'E': case 'o': case 'O': case 'c': case 'C': if (sscanf(&op[1], "%d.%d", &w, &h) != 2) { fprintf(stderr, "*** op: %s invalid\n", op); valid = FALSE; break; } if (w < 1 || (w & 1) == 0 || h < 1 || (h & 1) == 0 ) { fprintf(stderr, "*** op: %s; w = %d, h = %d; must both be odd\n", op, w, h); valid = FALSE; break; } /* fprintf(stderr, "op = %s; w = %d, h = %d\n", op, w, h); */ break; default: fprintf(stderr, "*** nonexistent op = %s\n", op); valid = FALSE; } LEPT_FREE(op); } if (!valid) { sarrayDestroy(&sa); return (PIX *)ERROR_PTR("sequence invalid", procName, NULL); } /* Parse and operate */ pixa = NULL; if (pdfout) { pixa = pixaCreate(0); pixaAddPix(pixa, pixs, L_CLONE); snprintf(buf, sizeof(buf), "/tmp/seq_output_%d.pdf", L_ABS(dispsep)); fname = genPathname(buf, NULL); } pixt1 = pixCopy(NULL, pixs); pixt2 = NULL; x = 0; for (i = 0; i < nops; i++) { rawop = sarrayGetString(sa, i, L_NOCOPY); op = stringRemoveChars(rawop, " \n\t"); switch (op[0]) { case 'd': case 'D': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_DILATE, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'e': case 'E': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_ERODE, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'o': case 'O': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_OPEN, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; case 'c': case 'C': sscanf(&op[1], "%d.%d", &w, &h); pixt2 = pixColorMorph(pixt1, L_MORPH_CLOSE, w, h); pixSwapAndDestroy(&pixt1, &pixt2); break; default: /* All invalid ops are caught in the first pass */ break; } LEPT_FREE(op); /* Debug output */ if (dispsep > 0) { pixDisplay(pixt1, x, dispy); x += dispsep; } if (pdfout) pixaAddPix(pixa, pixt1, L_COPY); } if (pdfout) { pixaConvertToPdf(pixa, 0, 1.0, L_FLATE_ENCODE, 0, fname, fname); LEPT_FREE(fname); pixaDestroy(&pixa); } sarrayDestroy(&sa); return pixt1; }