diff options
Diffstat (limited to 'editor/pbmclean.c')
-rw-r--r-- | editor/pbmclean.c | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/editor/pbmclean.c b/editor/pbmclean.c new file mode 100644 index 00000000..3ae3acfc --- /dev/null +++ b/editor/pbmclean.c @@ -0,0 +1,239 @@ +/* pbmclean.c - pixel cleaning. Remove pixel if less than n connected + * identical neighbours, n=1 default. + * AJCD 20/9/90 + * stern, Fri Oct 19 00:10:38 MET DST 2001 + * add '-white/-black' flags to restrict operation to given blobs + */ + +#include <stdio.h> +#include "pbm.h" +#include "shhopt.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespecs of input files */ + bool flipWhite; + bool flipBlack; + unsigned int connect; + unsigned int verbose; +}; + +#define PBM_INVERT(p) ((p) == PBM_WHITE ? PBM_BLACK : PBM_WHITE) + +/* input bitmap size and storage */ +static bit *inrow[3] ; + +#define THISROW (1) + +enum compass_heading { + WEST=0, + NORTHWEST=1, + NORTH=2, + NORTHEAST=3, + EAST=4, + SOUTHEAST=5, + SOUTH=6, + SOUTHWEST=7 +}; +/* compass directions from west clockwise. Indexed by enum compass_heading */ +int const xd[] = { -1, -1, 0, 1, 1, 1, 0, -1 } ; +int const yd[] = { 0, -1, -1, -1, 0, 1, 1, 1 } ; + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct3 opt; /* set by OPTENT3 */ + optEntry *option_def = malloc(100*sizeof(optEntry)); + unsigned int option_def_index; + + unsigned int black, white; + unsigned int minneighborsSpec; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + OPTENT3(0, "black", OPT_FLAG, NULL, &black, 0); + OPTENT3(0, "white", OPT_FLAG, NULL, &white, 0); + OPTENT3(0, "minneighbors", OPT_UINT, &cmdlineP->connect, + &minneighborsSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = TRUE; /* We sort of allow negative numbers as parms */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!black && !white) { + cmdlineP->flipBlack = TRUE; + cmdlineP->flipWhite = TRUE; + } else { + cmdlineP->flipBlack = !!black; + cmdlineP->flipWhite = !!white; + } + + + if (!minneighborsSpec) { + /* Now we do a sleazy tour through the parameters to see if + one is -N where N is a positive integer. That's for + backward compatibility, since Pbmclean used to have + unconventional syntax where a -N option was used instead of + the current -minneighbors option. The only reason -N didn't + get processed by pm_optParseOptions3() is that it looked + like a negative number parameter instead of an option. + If we find a -N, we make like it was a -minneighbors=N option. + */ + int i; + bool foundNegative; + + cmdlineP->connect = 1; /* default */ + foundNegative = FALSE; + + for (i = 1; i < argc; ++i) { + if (foundNegative) + argv[i-1] = argv[i]; + else { + if (atoi(argv[i]) < 0) { + cmdlineP->connect = - atoi(argv[i]); + foundNegative = TRUE; + } + } + } + if (foundNegative) + --argc; + } + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else if (argc-1 == 1) + cmdlineP->inputFilespec = argv[1]; + else + pm_error("You specified too many arguments (%d). The only " + "argument is the optional input file specification.", + argc-1); +} + + + + + +static void +nextrow(FILE * const ifd, + int const row, + int const cols, + int const rows, + int const format) { +/*---------------------------------------------------------------------------- + Advance one row in the input. + + 'row' is the row number that will be the current row. +-----------------------------------------------------------------------------*/ + bit * shuffle; + + /* First, get the "next" row in inrow[2] if this is the very first + call to nextrow(). + */ + if (inrow[2] == NULL && row < rows) { + inrow[2] = pbm_allocrow(cols); + pbm_readpbmrow(ifd, inrow[2], cols, format); + } + /* Now advance the inrow[] window, rotating the buffer that now holds + the "previous" row to use it for the new "next" row. + */ + shuffle = inrow[0]; + + inrow[0] = inrow[1]; + inrow[1] = inrow[2]; + inrow[2] = shuffle ; + if (row+1 < rows) { + /* Read the "next" row in from the file. Allocate buffer if needed */ + if (inrow[2] == NULL) + inrow[2] = pbm_allocrow(cols); + pbm_readpbmrow(ifd, inrow[2], cols, format); + } else { + /* There is no next row */ + if (inrow[2]) { + pbm_freerow(inrow[2]); + inrow[2] = NULL; + } + } +} + + + +static unsigned int +likeNeighbors(bit * const inrow[3], + unsigned int const col, + unsigned int const cols) { + + int const point = inrow[THISROW][col]; + enum compass_heading heading; + int joined; + + joined = 0; /* initial value */ + for (heading = WEST; heading <= SOUTHWEST; ++heading) { + int x = col + xd[heading] ; + int y = THISROW + yd[heading] ; + if (x < 0 || x >= cols || !inrow[y]) { + if (point == PBM_WHITE) joined++; + } else if (inrow[y][x] == point) joined++ ; + } + return joined; +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + FILE *ifp; + bit *outrow; + int cols, rows, format; + unsigned int row; + unsigned int nFlipped; /* Number of pixels we have flipped so far */ + + pbm_init( &argc, argv ); + + parseCommandLine(argc, argv, &cmdline); + + ifp = pm_openr(cmdline.inputFilespec); + + inrow[0] = inrow[1] = inrow[2] = NULL; + pbm_readpbminit(ifp, &cols, &rows, &format); + + outrow = pbm_allocrow(cols); + + pbm_writepbminit(stdout, cols, rows, 0) ; + + nFlipped = 0; /* No pixels flipped yet */ + for (row = 0; row < rows; ++row) { + unsigned int col; + nextrow(ifp, row, cols, rows, format); + for (col = 0; col < cols; ++col) { + bit const thispoint = inrow[THISROW][col]; + if ((cmdline.flipWhite && thispoint == PBM_WHITE) || + (cmdline.flipBlack && thispoint == PBM_BLACK)) { + if (likeNeighbors(inrow, col, cols) < cmdline.connect) { + outrow[col] = PBM_INVERT(thispoint); + ++nFlipped; + } else + outrow[col] = thispoint; + } else + outrow[col] = thispoint; + } + pbm_writepbmrow(stdout, outrow, cols, 0) ; + } + pbm_freerow(outrow); + pm_close(ifp); + + if (cmdline.verbose) + pm_message("%d pixels flipped", nFlipped); + + return 0; +} |