home *** CD-ROM | disk | FTP | other *** search
- /*
- * xcc.h
- *
- * Practical Algorithms for Image Analysis
- *
- * Copyright (c) 1997, 1998, 1999 MLMSoftwareGroup, LLC
- */
-
- /*
- * XC(enter)C(ircular disks)
- *
- * determine, in an efficient manner, (estimates of) center positions
- * for circular objects by taking advantage of knwoledge about the
- * circular object shape and object size, the latter in the form of
- * a known radius, R, or mean and variance of the distribution of radii:
- *
- * speed enhancements derive from two principal sources:
- * -- i) the (vertical) interval, DELTA_i, of the raster scan performed
- * to locate object boundaries is set in accordance with
- * the value supplied for D == 2*R (or <D>) so as to sample
- * each object in at least n (and at most n+1) successive scans:
- * DELTA_i is thus simply given as D/n; n may be adjusted to reflect
- * the fidelity of the object (``disk'') contour: for a monodisperse
- * distribution of perfect binary disks n might be set to 2 for
- * greatest gain in computatinal efficiency, while a noisy contour
- * might necessitate a higher sampling frequency, or greater n, to
- * ensure a better estimate of the center position, derived from a
- * set of 2*n (maximally 2*(n+1)) contour points;
- *
- * --ii) assuming circular shapes, the center position is determined
- * from a set of boundary points, implying an associated complexity
- * O(N), N denoting the number of object pixels, as opposed to the
- * O(N**2) dependence of template matching methods
- *
- */
-
- #include "xcc.h"
-
- #define TESTSET 100 /* test data set containing chords */
- #define TOTAL_DISK TESTSET
-
- #undef DEBUG
- #undef DPRINT
- #undef DBG_MEM
-
- #undef SHOW_ETLL
- #undef CHECK_ETLL
- #undef CHECK_DLL
-
- #define NO_DISPLAY -1
- #define TERSE 0
- #define VERBOSE 1
-
-
- #define DISPL_PAGE 1
- #define RESET 1
- #define NO_RESET 0
-
- #define BORDER_WIDTH 1 /* zero border width (in pix), default value */
- #define EDGE_FILTER_LEN 5 /* default leng of 1d edge detection filter */
- #define NCH_MIN 2 /* min nof scan lines (chords) per disk */
- #define CONTOUR_COLOR 127
- #define UPDATE_DL 6 /* update disk dia, based on _DL proc disks */
-
- #define OFF 0
- #define ON 1
-
-
- #define F_TO_INT(a) ( ((a)-(int)(a)>0.5) ? ((int)(a)+1) : ((int)(a)) )
-
-
- /*
- * global variables
- */
- extern char *optarg;
- extern int optind, opterr;
-
-
- int WRITE_FILE = 0;
- int DISP_MODE = TERSE; /* VERBOSE, TERSE or NO_DISPLAY */
- int TEST_INPUT = 0;
- int SCAN_IMG = 1;
- int DISPL_DISK_BDY = 1; /* when set, display convex hull */
- int ZB = -1;
- int R_DSK = -1; /* disk radius estimate supplied ? */
- int VERT_SI = -1; /* vertical scan interval supplied ? */
- char *IMG_TYPE = "B"; /* img type: B or G */
- int GRAY_LEV = 0; /* BNRY(0)/GRTN(1) */
- int UPDATE_DDIA = 1; /* when set, update disk dia */
-
-
- int INP_FILE_TYPE = -1; /* input file types */
- int TPL = 0;
-
- FILE *fpIn, *fpOut;
-
- int del_ir; /* vertical scan interval */
- float drad, ddia; /* estimated disk radius, diameter */
-
-
- /*
- * display disk contours
- */
- void
- displ_disk_contours (struct linklist *list, Image * imgIO, int value)
- {
- struct disk *cdsk;
- int nd;
-
- llhead (list);
- if ((nd = ll_length (list)) != 0) { /* list not empty */
- #ifdef DPRINT
- gprintf (fpOut, "\n...display disk contours\n");
- #endif
- do {
- if ((cdsk = (struct disk *) list->clp->item)->Type == Accept) {
-
- draw_circle (cdsk->ctr.x, cdsk->ctr.y, cdsk->rad, imgIO, value);
- }
-
- } while (llnext (list) == True);
- }
- #ifdef DPRINT
- else
- gprintf (fpOut, " list empty\n");
- #endif
- }
-
- /*
- * initialize disk list
- *
- * note: matching function to be set by call to llsetmatch() in code as needed
- */
- void
- init_dll (struct linklist *disk_list)
- {
-
- llzero (disk_list); /* init empty list */
- llsetsize (sizeof (struct disk), disk_list);
- }
-
-
- /*
- * initialize edge_tuple list
- *
- * note: matching function to be set by call to llsetmatch() in code as needed
- */
- void
- init_etll (struct linklist *edge_tuple_list)
- {
-
- llzero (edge_tuple_list); /* init empty list */
- llsetsize (sizeof (struct edge_tuple), edge_tuple_list);
- }
-
-
-
- /*
- * deallocate memory occupied by links in a list
- */
- int
- rm_llistlink (struct linklist *list)
- {
- int link_count;
-
- link_count = 0;
- llhead (list);
- do {
- lldelete (list);
- link_count++;
-
- } while (list->listlength != 0);
-
- return (link_count);
- }
-
-
- /*
- * display edge tuple list
- */
- void
- show_etpl_list (struct linklist *list)
- {
- struct edge_tuple *cetpl;
- int ie = 0, ne;
-
- llhead (list);
- if ((ne = ll_length (list)) != 0) { /* list not empty */
- #ifdef DPRINT
- gprintf (fpOut, "\n...list of %d edge tuples\n", ne);
- #endif
- do {
- cetpl = (struct edge_tuple *) list->clp->item;
- #ifdef DPRINT
- gprintf (fpOut, " (%3d, %3d); status: %2d\n",
- cetpl->cl, cetpl->cr, (int) cetpl->status);
- if ((ie + 1) % 8 == 0) {
- gprintf (fpOut, "\n");
- ie = 0;
- }
- #endif
- ie++;
- } while (llnext (list) == True);
- }
- #ifdef DPRINT
- else
- gprintf (fpOut, " list empty\n");
- #endif
- }
-
-
-
- /*
- * display list (terse version)
- */
- void
- tshow_disk_list (struct linklist *list)
- {
- struct disk *cdsk;
- int id = 0, nd;
-
- llhead (list);
- if ((nd = ll_length (list)) != 0) { /* list not empty */
- gprintf (fpOut, "\n...list of %d disk centers\n", nd);
- do {
- cdsk = (struct disk *) list->clp->item;
- gprintf (fpOut, " (%3d, %3d)", cdsk->ctr.x, cdsk->ctr.y);
- if ((id + 1) % 8 == 0) {
- gprintf (fpOut, "\n");
- id = 0;
- }
- id++;
- } while (llnext (list) == True);
- }
- else
- gprintf (fpOut, " list empty\n");
- }
-
-
- /*
- * display list
- */
- void
- show_disk_list (struct linklist *list)
- {
- struct disk *cdsk;
- int id = 0, nd;
- int ich;
-
- llhead (list);
- if ((nd = ll_length (list)) != 0) { /* list not empty */
- gprintf (fpOut, "\n...list of %d disk structures\n", nd);
- do {
- cdsk = (struct disk *) list->clp->item;
- gprintf (fpOut, "\n %3d chord(s) (r; cl, cr):\n", cdsk->nch);
- for (ich = 0; ich < cdsk->nch; ich++) {
- gprintf (fpOut, " (%3d; %3d, %3d)\n",
- cdsk->chords[ich].r,
- cdsk->chords[ich].cl,
- cdsk->chords[ich].cr);
- }
- gprintf (fpOut, " center: (%3d, %3d)",
- cdsk->ctr.x, cdsk->ctr.y);
- gprintf (fpOut, "...radius: %3d", cdsk->rad);
- gprintf (fpOut, "...status: %d\n", cdsk->Status);
-
- } while (llnext (list) == True);
- }
- else
- gprintf (fpOut, " list empty\n");
- }
-
-
- /*
- * update initial estimate of disk diameter
- */
- float
- update_disk_dia (struct linklist *list)
- {
- struct disk *cdsk;
- float odd, dd = (float) 0.0;
- int nd;
-
- odd = ddia;
- dd = (float) 0.0;
- nd = 0;
- llhead (list);
- do {
- cdsk = (struct disk *) list->clp->item;
-
- if (cdsk->nch > 1) {
- dd += (float) 2.0 *cdsk->rad;
- nd++;
- }
-
- } while (llnext (list) == True);
- dd /= (float) nd;
-
- if (fabs ((dd - ddia) / ddia) > 0.1)
- return (dd);
- else
- return (odd);
- }
-
-
-
-
- /*
- * gprintf() functions:
- * write to std output and, if option WRITE_FILE set, to file wbuf (stream)
- */
- void
- gprintf (FILE * fpOut, char *fmt,...)
- {
- va_list arg_ptr;
-
- va_start (arg_ptr, fmt);
- vprintf (fmt, arg_ptr);
- if (WRITE_FILE == 1)
- vfprintf (fpOut, fmt, arg_ptr);
- va_end (arg_ptr);
-
- }
-
-
-
- /*
- * error message
- */
- void
- exitmess (char *prompt, int status)
- {
- printf (prompt);
- printf ("\n");
-
- exit (status);
- }
-
- /*
- * usage of routine
- */
- void
- usage (char *progname)
- {
- progname = last_bs (progname);
- printf ("USAGE: %s inimg outimg [-t file] [-w file] [-s rad] [-n lines]\n", progname);
- printf (" [-b] [-z pix] [-i imgtype] [-f len] [-L]\n");
- printf ("\n%s determines, in an efficient manner, (estimates of) center positions\n", progname);
- printf ("for circular objects by taking advantage of knwoledge about the\n");
- printf ("circular object shape and object size, the latter in the form of\n");
- printf ("known radius, R, or mean and variance of the distribution of radii.\n\n");
- printf ("ARGUMENTS:\n");
- printf (" inimg: input image filename (TIF)\n");
- printf (" outimg: output image filename (TIF)\n\n");
- printf ("OPTIONS:\n");
- printf (" -t file: read test input from file fn ( .tpl) -s, -n options disabled\n");
- printf (" -w file: write disk parameters to file fn.dsk\n");
- printf (" -s rad: supply estimated (mean) disk radius (in pix)\n");
- printf (" -n lines: supply min nof scan lines to sample each disk\n");
- printf (" (must exceed %d)\n", NCH_MIN);
- printf (" -b: do not display disk bdy\n");
- printf (" -z pix: zero a border strip of width pix (default: %d)\n", BORDER_WIDTH);
- printf (" -i str: specify image type, B(BINARY) (default) or G (GRAY) \n");
- printf (" -f len: specify edge filter len, (odd)l (default: %d)\n", EDGE_FILTER_LEN);
- printf (" -L: print Software License for this module\n");
- exit (1);
- }
-
- void
- main (int argc, char **argv)
- {
- Image *imgIn;
- static char *inp_suffix =
- {".tpl"}; /* default inp file suffix */
- static char *tpl_type =
- {".tpl"}; /* suffix for .pdt inp file */
-
- static char *wsuffix =
- {".dsk"}; /* suffix for output file name */
- static char rdbuf[32], *buf = &rdbuf[0];
- static char wrbuf[32], *wbuf = &wrbuf[0];
- int ic, is;
- int i_arg;
-
- /* list structs */
- struct linklist etll; /* list of edge tuples on current scan line */
- int ne;
-
- struct linklist dll; /* disk (linked) list */
- #ifdef DEBUG
- struct linklist *cdll; /* ptr to current dll */
- #endif
- int rm_link = 0;
-
-
- int nds;
- int nch;
-
- /* AOI dimensions */
- int ir, ir_base;
- int nrw, ncl;
- int jmin, imin, jmax, imax;
- int left_x, right_x;
- int strip_wdth = BORDER_WIDTH;
- unsigned char *row;
- int nf = EDGE_FILTER_LEN;
- float *ef;
-
- /*
- * cmd line options ( see usage() ):
- */
- static char *optstring = "t:w:s:n:z:bi:f:L";
-
-
- /*
- * parse command line
- */
- optind = 3;
- opterr = 1; /* give error messages */
-
- if (argc < 2)
- usage (argv[0]);
-
- while ((i_arg = getopt (argc, argv, optstring)) != EOF) {
- switch (i_arg) {
-
- case 't':
- printf ("...option %c: ", i_arg);
- printf (" employ test data in file %s\n", buf = optarg);
- TEST_INPUT = 1;
- SCAN_IMG = 0;
- UPDATE_DDIA = 0;
-
- /* get size of data set from file */
- if ((fpIn = fopen (buf, "r")) == NULL) {
- printf ("\n...could not open input file\n");
- exit (1);
- }
-
- /* strip file suffix and determine file type */
- ic = 0;
- while (*(buf + ic) != '.')
- ic++;
- for (is = 0; is < 4; is++)
- *(inp_suffix + is) = *(buf + ic + is);
-
- if (strncmp (inp_suffix, tpl_type, 4) == 0) {
- INP_FILE_TYPE = TPL;
- }
-
- /* initialize size variables */
- fetch_test_parms (fpIn, &ddia, &del_ir, &nch, &ir_base);
- printf ("\n...test parameters retrieved: ");
- printf (" disk dia: %f\n", ddia);
- printf (" vert scan interval: %3d\n", del_ir);
- printf (" corresp vert scan freq: %3d\n", nch);
-
- R_DSK = 1;
- VERT_SI = 1;
- break;
- case 'w':
- printf ("...option %c: ", i_arg);
- printf (" write disk data to file %s\n",
- wbuf = optarg);
- if ((fpOut = fopen (wbuf, "w")) == NULL) {
- printf ("\n...cannot open %s for writing\n",
- wbuf);
- exit (1);
- }
- gprintf (fpOut, "...logging params in file %s\n", wbuf);
- WRITE_FILE = 1;
- break;
- case 's':
- printf ("...option %c: ", i_arg);
- if (TEST_INPUT == 0) {
- printf (" supply estimate of disk RADIUS: %f\n",
- drad = (float) atof (optarg));
- ddia = (float) 2.0 *drad;
- R_DSK = 1;
- if (VERT_SI == -1) {
- nch = NCH_MIN;
- printf (" -->default vert scan interval: %3d\n",
- del_ir = F_TO_INT (drad) - 1);
- }
- }
- else
- printf (" TEST_INPUT -- option disabled\n");
- break;
- case 'n':
- printf ("...option %c: ", i_arg);
- if (TEST_INPUT == 0) {
- printf (" set vert scan freq (min scan lns/disk): ");
- printf ("nch = %d\n", nch = atoi (optarg));
- if (R_DSK == -1) {
- printf ("\n...must first supply estimate for (mean) disk radius\n");
- exit (1);
- }
- printf (" -->implied vert scan interval: %3d\n",
- del_ir = F_TO_INT (2.0 * drad / (float) nch));
-
- if (nch * del_ir == F_TO_INT (ddia)) {
- nch++;
- printf (" -->adj vert scan freq: ");
- printf (" max scan lns/dsk: %d\n", nch);
- }
- VERT_SI = 1;
- }
- else
- printf (" TEST_INPUT -- option disabled\n");
- break;
- case 'b':
- printf ("...option %c:", i_arg);
- printf (" do not display disk boundaries\n");
- DISPL_DISK_BDY = 0;
- break;
- case 'z':
- printf ("...option %c:", i_arg);
- printf (" zero a border strip of width %d\n",
- strip_wdth = atoi (optarg));
- ZB = 1;
- break;
- case 'i':
- printf ("...option %c:", i_arg);
- printf (" process %s image\n", IMG_TYPE = optarg);
- if (strncmp (IMG_TYPE, "B", 1) == 0)
- GRAY_LEV = 0;
- else if (strncmp (IMG_TYPE, "G", 1) == 0)
- GRAY_LEV = 1;
- else
- exitmess ("\n...unknown image type\n", 1);
- break;
- case 'f':
- printf ("...option %c:", i_arg);
- printf (" select edge filter length: ");
- if ((nf = atoi (optarg)) % 2 == 0)
- nf++;
- printf ("%2d\n", nf);
- break;
- case 'L':
- print_sos_lic ();
- exit (0);
- default:
- printf ("\n...unknown condition encountered\n");
- exit (1);
- break;
- }
- }
- if (R_DSK == -1) {
- printf ("\n...must supply estimate for (mean) disk radius\n");
- exit (1);
- }
-
-
-
-
- /*
- * Read input image
- */
- imgIn = ImageIn (argv[1]);
-
- if (imgIn->bps == 8 && imgIn->spp == 3) {
- printf ("Got RGB image!!!\nInput image must be Grayscale or B&W!!\n");
- exit (1);
- }
-
- /*
- * scan full frame (if AOI desired, supply boundary coordinates here)
- */
- jmin = imin = 0;
- jmax = imgIn->width;
- imax = imgIn->height;
-
- left_x = jmin;
- right_x = jmax - 1;
- ncl = jmax - jmin;
- nrw = imax - imin;
-
- #ifdef DEBUG
- printf ("\n...ncl = %d, nrw = %d\n", ncl, nrw);
- #endif
-
- /*
- * zero outer border of image (-->select one method)
- */
- if (ZB == 1)
- zero_border (imgIn, strip_wdth);
-
-
- /*
- * display parameter settings employing global printf function;
- * open log file
- */
- gprintf (fpOut, "\nparameter settings:\n");
- gprintf (fpOut, " image type: %s\n", IMG_TYPE);
- gprintf (fpOut, "vert scan interval (del_ir): %3d\n", del_ir);
- gprintf (fpOut, " disk dia: %f\n", ddia);
-
- /*
- * memory allocation
- */
- if ((row = (unsigned char *) calloc (ncl, sizeof (unsigned char))) == NULL)
- exitmess ("mem alloc for row failed", 1);
-
-
- if (TEST_INPUT == 1) {
- gprintf (fpOut, "\n...process test data set in %s\n", buf);
- }
- else if (SCAN_IMG == 1) {
- gprintf (fpOut, "\n...scan image\n");
-
- if (GRAY_LEV == 1) {
- if ((ef = (float *) calloc (nf, sizeof (float))) == NULL)
- exitmess ("mem alloc for edge filter ef failed", 1);
-
- fetch_edge_det (ef, nf);
- }
- }
-
-
- /*
- * initialize (empty) lists
- */
- init_etll (&etll);
- init_dll (&dll);
-
-
-
- #ifdef DEBUG
- cdll = &dll;
- printf ("\n...structure parameters for disk list:\n");
- printf ("sizeof(struct linklist): %d\n", sizeof (struct linklist));
- printf (" item length: %d\n", cdll->itemlength);
- printf (" cur list length: %d\n", cdll->listlength);
- #endif
-
-
- /*
- * loop over (selected) rows
- * for each row index: scan row, applying desired (1d) edge filter,
- * and construct list of edge tuples;
- *
- * following completion of scan, update disk list by matching the
- * ``runs'' of ON-pixels, given by the set of edge tuples {yl, yr},
- * to the those entries in the disk list which remain ACTIVE:
- * if no match is found, the respective disk entry in the list
- * is closed (set to INACTIVE) and a new ACTIVE disk is opened
- *
- * matching is accmplished by examining ``symmetric'' overlap of
- * runs in successive scans, as detailed below (see function
- * update_dll() )
- */
-
- if (TEST_INPUT == OFF)
- ir_base = strip_wdth + 1;
- ir = ir_base;
- nds = 0; /* current number of disks */
- do {
- ne = 0; /* nof ON segm (edge tpls) per row */
- init_etll (&etll);
-
- /* scan image row */
- if (TEST_INPUT == 1) {
- ne = fetch_test_row (fpIn, &etll);
- }
- else if (SCAN_IMG == 1) {
- getrow (row, ir, imgIn);
-
- if (GRAY_LEV == 1)
- edge_det (row, ncl, ef, nf);
- #ifdef DPRINT
- printf ("...about to encode row %d\n", ir);
- #endif
- ne = encode_row (row, ncl, &etll, GRAY_LEV);
- }
-
- #ifdef SHOW_ETLL
- printf ("\n...row %3d: ", ir);
- show_etpl_list (&etll);
- #endif
-
- if (ne > 0) {
- nds += update_disk_list (ir, nch, &dll, &etll);
-
- #ifdef CHECK_DLL
- show_disk_list (&dll);
- #endif
-
-
- /* clear edge tuple list */
- rm_link = rm_llistlink (&etll);
-
- #ifdef DPRINT
- printf ("...%d entries removed from edge tuple list\n", rm_link);
- #endif
- }
-
- /* update initial estimate of disk diameter */
- if ((UPDATE_DDIA == ON) && (ll_length (&dll) >= UPDATE_DL)) {
- #ifdef DPRINT
- printf ("\n...update init estimate of disk diameter\n");
- #endif
-
- ddia = update_disk_dia (&dll);
-
- del_ir = F_TO_INT (ddia / (float) nch);
- if (nch * del_ir >= (int) ddia)
- nch++;
-
- #ifdef DPRINT
- printf (" -->new estimate of disk dia: %f\n", ddia);
- printf (" -->new vert scan interval: %3d\n", del_ir);
- printf (" -->new vert scan freq: %2d\n", nch);
- #endif
-
- UPDATE_DDIA = OFF;
- }
-
-
- ir += del_ir;
-
- } while (ir < nrw - (ir_base + 1));
-
-
- /*
- * display results
- */
- #ifdef DPRINT
- printf ("\n...done with construction of disk list:\n");
-
- if (DISP_MODE == VERBOSE)
- show_disk_list (&dll);
- else if (DISP_MODE == TERSE)
- tshow_disk_list (&dll);
- #endif
- /*
- * overlay disk contours onto original image
- */
- if (DISPL_DISK_BDY == ON) {
- displ_disk_contours (&dll, imgIn, GRAY);
- }
-
-
- /*
- * deallocate memory assigned to lists
- */
- #ifdef DPRINT
- printf ("\n...deallocating memory for linked lists\n");
- printf (" disks:\n");
- #endif
- rm_link = rm_llistlink (&dll);
- #ifdef DBG_MEM
- printf (" %d links removed from disk list\n", rm_link);
- #endif
-
-
- /*
- * deallocate memory and close files
- */
- free (row);
- if (GRAY_LEV == 1)
- free (ef);
-
- if (TEST_INPUT == 1)
- fclose (fpIn);
- if (WRITE_FILE == 1)
- fclose (fpOut);
- /*
- * Write the output image
- */
- printf ("...writing image file %s\n", argv[2]);
- ImageOut (argv[2], imgIn);
-
- }
-