home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_11_08 / 1108036b < prev    next >
Text File  |  1993-05-05  |  23KB  |  582 lines

  1. /***************************************************************
  2.  * file: ZONE.C
  3.  * purpose: text region detection via cellular automata
  4.  **************************************************************/
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <errno.h>
  10. #include "zone.h"
  11.  
  12. /* local data */
  13. static ZONE zone_list[MAX_ZONES];   /* this could be dynamically allocated */
  14. static int zone_count;
  15.  
  16.  
  17. /* local prototypes */
  18. static unsigned char *sample_page(int *dx,int *dy,int *samplex,int *sampley);
  19. static void cut_vertical_lines(unsigned char *image,int dx,int dy);
  20. static void block_zones(unsigned char *image,int dx,int dy,int coarseness);
  21. static void sequence_zones(unsigned char *image,int dx,int dy,int order);
  22. static int extract_zone(unsigned char *image,int dx,int dy,int x,int y,ZONE *zone_ptr);
  23. static void overlap_zones(ZONE *zone_array,int *array_size);
  24.  
  25.  
  26. /************************************************
  27.  * function: int zone(int coarseness)
  28.  *  The process steps are:
  29.  *      1) Sample the page
  30.  *      2) Cut vertical lines from the page
  31.  *      3) Block out zones via cellular automata
  32.  *      4) Extract the zones
  33.  *      5) Sequence zones
  34.  * parameters: coarseness value 0-5, order (COLUMN or ROW)
  35.  * returns: 1 if OK or 0 if error,  see errno
  36.  ************************************************/
  37. int zone(int coarseness,int order)
  38.     {
  39.     unsigned char *image;
  40.     int dx,dy;
  41.     int samplex,sampley;
  42.     int i;
  43.  
  44.     if (coarseness < 0 || coarseness > 5)
  45.         {               /* test coarseness parameter */
  46.         errno = EINVAL;
  47.         return 0;
  48.         }
  49.                         /* get a scaled copy of the page */
  50.     image = sample_page(&dx,&dy,&samplex,&sampley);
  51.     if (image == NULL)                      /* memory? */
  52.         return 0;
  53. #if CUT_VERTICAL_LINES
  54.     cut_vertical_lines(image,dx,dy);        /* remove boxes around text */
  55. #endif
  56.     block_zones(image,dx,dy,coarseness);    /* block out zones */
  57.     sequence_zones(image,dx,dy,order);
  58.     for (i = 0 ; i < zone_count ; i++)
  59.         {                                   /* translate to full page */
  60.         zone_list[i].x1 *= samplex;
  61.         zone_list[i].y1 *= sampley;
  62.         zone_list[i].x2 *= samplex;
  63.         zone_list[i].y2 *= sampley;
  64.         }
  65.     free(image);                            /* clean up */
  66.     return 1;
  67.     }
  68.  
  69.  
  70. /**************** LOCAL FUNCTIONS ****************/
  71.  
  72. /************************************************
  73.  * function: static unsigned char *sample_page(int *dx,int *dy,int *samplex,int *sampley)
  74.  *  Sample the page.  Normally, the entire page is stored in memory.  Since
  75.  *  the memory requirements are typically a megabyte, the page, in DOS
  76.  *  machines, is somewhere in extended memory.  So that this demo can
  77.  *  work on machines lacking extended memory, I sampled the page when I
  78.  *  scaled it for display.  See display_sample() below.  However, I
  79.  *  have #ifed around the code that is normally used to sample the page
  80.  *  from the memory image.  You need to provide two functions, as well as
  81.  *  the extended memory handler.  They are void memory_info(DISPLAY_BOX *);
  82.  *  which gets the x/y extents of the image, and  (unsigned char *
  83.  *  memory_ptr(int y); which returns a pointer to a scan line in the memory
  84.  *  image.  Sample_page() creates a standardized, reduced image suitable for
  85.  *  cellular automation.  The sample has each byte, not bit, representing a
  86.  *  pixel area.  The byte is 0xff if black or 0x00 if white.
  87.  *  The sampling procedure is important for region finding.  If possible
  88.  *  it should be a function of the density of the original image.  If
  89.  *  the image isn't square, for example a 200x100 fax file, then the
  90.  *  x and y sampling should be adjusted accordingly.  Since I don't
  91.  *  have dpi information here, I am scaling it to a typical, 200dpi,
  92.  *  value after it was adjusted for screen display.
  93.  *  Note: Bit 7 is leftmost and 0 is rightmost; 1 bits are black, 0 are white.
  94.  * parameters: pointers to storage for the byte width and height of the sampled
  95.  *              image and the sample bit distance in x and y
  96.  * returns: pointer to sampled page or NULL if no memory
  97.  ************************************************/
  98. static unsigned char *sample_page(int *dx,int *dy,int *samplex,int *sampley)
  99.     {
  100.     static unsigned char bit_mask[] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
  101.     unsigned char *image,*line_ptr,*line_ptr2,*buff_ptr;
  102.     DISPLAY_BOX file;
  103.     unsigned int x,y,width,height;
  104.  
  105.     memory_info(&file);     /* need to provide this, gets file dimensions */
  106.                             /* from memory image of file */
  107.     *samplex = SAMPLE_200_X;        /* sample sizes */
  108.     *sampley = SAMPLE_200_Y;
  109.     *dx = file.width / *samplex;    /* extent of sample */
  110.     *dy = file.height / *sampley;
  111.     while (((long)*dx * (long)*dy) > MAX_MALLOC)
  112.         {           /* adjust sampling to fit memory restrictions */
  113.         (*samplex)++;
  114.         (*sampley)++;
  115.         *dx = file.width / *samplex;
  116.         *dy = file.height / *sampley;
  117.         }
  118.     if ((image = malloc(*dx * *dy)) == NULL)    /* allocate sample buffer */
  119.         return NULL;
  120.     memset(image,WHITE,*dx * *dy);          /* set to white */
  121.     width = *dx * *samplex;
  122.     height = *dy * *sampley;
  123.     if (*samplex >= 8)
  124.         {                                   /* byte sampling */
  125.         for (y = 0, buff_ptr = image ; y < height ; y += *sampley)
  126.             {                               /* for each y sample */
  127.                         /* need to provide memory_ptr which gets a pointer
  128.                          * to a scan line from the memory image of the file */
  129.             line_ptr = memory_ptr(y);
  130.             line_ptr2 = memory_ptr(y + *sampley/2); /* double sample in y */
  131.             for (x = 0 ; x < width ; x += *samplex, buff_ptr++)
  132.                 if (*(line_ptr+(x>>3)) | *(line_ptr2+(x>>3)))
  133.                     *buff_ptr = BLACK;  /* if byte has black, set sample */
  134.             }
  135.         }
  136.     else
  137.         {                                   /* bit sampling */
  138.         for (y = 0, buff_ptr = image ; y < height ; y += *sampley)
  139.             {                               /* for each y sample */
  140.                         /* need to provide memory_ptr which gets a pointer
  141.                          * to a scan line from the memory image of the file */
  142.             line_ptr = memory_ptr(y);
  143.             line_ptr2 = memory_ptr(y + *sampley/2); /* double sample in y */
  144.             for (x = 0 ; x < width ; x += *samplex, buff_ptr++)
  145.                 if ((*(line_ptr+(x>>3)) | *(line_ptr2+(x>>3))) & bit_mask[x&7])
  146.                     *buff_ptr = BLACK;      /* if bit is black, set sample */
  147.             }
  148.         }
  149.     return image;
  150.     }
  151.  
  152.  
  153. #if CUT_VERTICAL_LINES
  154. /************************************************
  155.  * function: static void cut_vertical_lines(unsigned char *image,int dx,int dy)
  156.  *  Remove vertical lines from sample.  The purpose of this function is to
  157.  *  unbox text.  Removing the vertical box lines accomplishes this.  Trying
  158.  *  to remove the horizontal lines is dangerous because you might also remove
  159.  *  the text.
  160.  * parameters: pointer to sampled image buffer and x/y extents of buffer
  161.  * returns: nothing
  162.  ************************************************/
  163. static void cut_vertical_lines(unsigned char *image,int dx,int dy)
  164.     {
  165.     int x,y,count,y1;
  166.     unsigned char *ptr,*qtr;
  167.  
  168.     for (x = 0 ; x < dx ; x++)      /* scan image left to right */
  169.         {
  170.         ptr = image+x;
  171.         count = 0;
  172.         for (y = 0 ; y < dy ; y++, ptr += dx)
  173.             {                   /* scan up and down counting line pixels */
  174.             if (*ptr)
  175.                 count++;
  176.             else
  177.                 count = 0;
  178.             if (count >= VERTICAL_LINE_SIZE)
  179.                 {               /* we have a veritcal line */
  180.                 for (y1=y, qtr=ptr ; *ptr!=WHITE && y>=0 ; y--, ptr-=dx)
  181.                     *ptr = WHITE;   /* white out moving up */
  182.                 for (y=y1+1, ptr=qtr+dx ; *ptr!=WHITE && y<dy ; ptr+=dx)
  183.                     *ptr = WHITE;   /* white out moving down */
  184.                 count = 0;      /* reset counter */
  185.                 }
  186.             }
  187.         }
  188.     }
  189. #endif
  190.  
  191.  
  192. /************************************************
  193.  * function: static void block_zones(unsigned char *image,int dx,int dy,int coarseness)
  194.  *  Use cellular automata to create blocks from image.  Each block represents
  195.  *  a region of text.  There are two steps to the process.  First, use cellular
  196.  *  automata to sketch the reason.  Second, use the coarseness (0-5) to coalesce
  197.  *  the regions.  A higher coarseness value will make larger zones.
  198.  * parameters: pointer to image buffer, x/y extents of buffer and coarseness
  199.  *             of zoning (0-5)
  200.  * returns: nothing
  201.  ************************************************/
  202. static void block_zones(unsigned char *image,int dx,int dy,int coarseness)
  203.     {
  204.     int i,j,x,y,score;
  205.     unsigned char *line,*pixel;
  206.  
  207.     for (y=1, line=image+dx ; y < dy-1 ; y++, line+=dx)
  208.         {   /* walk through buffer scanning for neighbors */
  209.         for (x=1, pixel=line+1 ; x < dx-1 ; x++, pixel++)
  210.             {
  211.             score = 0;
  212.             if (*(pixel-1) & ALIVE)     /* left */
  213.                 score++;
  214.             if (*(pixel+1) & ALIVE)     /* right */
  215.                 score++;
  216.             if (*(pixel-dx) & ALIVE)    /* up */
  217.                 score++;
  218.             if (*(pixel+dx) & ALIVE)    /* down */
  219.                 score++;
  220.             if (*pixel == WHITE && score >= LIFE_SCORE)
  221.                 *pixel = PREGNANT;
  222.             else if (*pixel == BLACK && score <= DEATH_SCORE)
  223.                 *pixel = SICK;
  224.             }
  225.         }
  226.     for (pixel = image ; pixel < image + dy * dx ; pixel++)
  227.         {   /* birth and bury */
  228.         if (*pixel == PREGNANT)
  229.             *pixel = BLACK;
  230.         else if (*pixel == SICK)
  231.             *pixel = WHITE;
  232.         }
  233.     if (coarseness <= 0)        /* no need to coalesce */
  234.         return;
  235.     /* coalesce regions, based on coarseness, i.e. coarseness == 2
  236.      * will close all horizontal/vertical gaps of 2 pixels */
  237.     for (y=0, line=image+1 ; y < dy ; y++, line+=dx)
  238.         {   /* coalesce horizontally */
  239.         for (x=1, pixel=line ; x < dx-coarseness ; x++, pixel++)
  240.             {
  241.             if (*pixel == WHITE && *(pixel-1) == BLACK)
  242.                 {
  243.                 for (i = 1 ; i <= coarseness ; i++)
  244.                     {
  245.                     if (*(pixel+i))
  246.                         {
  247.                         for (i = 0 ; i < coarseness ; i++, pixel++)
  248.                             *pixel = BLACK;
  249.                         pixel--;
  250.                         x += coarseness-1;
  251.                         break;
  252.                         }
  253.                     }
  254.                 }
  255.             }
  256.         }
  257.     for (x=0, line=image+dx ; x < dx ; x++, line++)
  258.         {   /* coalesce vertically */
  259.         for (y=1, pixel=line ; y < dy-coarseness ; y++, pixel+=dx)
  260.             {
  261.             if (*pixel == WHITE && *(pixel-dx) == BLACK)
  262.                 {
  263.                 for (i=1, j=dx ; i <= coarseness ; i++, j+=dx)
  264.                     {
  265.                     if (*(pixel+j))
  266.                         {
  267.                         for (i = 0 ; i < coarseness ; i++, pixel+=dx)
  268.                             *pixel = BLACK;
  269.                         pixel -= dx;
  270.                         y += (coarseness-1);
  271.                         break;
  272.                         }
  273.                     }
  274.                 }
  275.             }
  276.         }
  277.     }
  278.  
  279.  
  280. /************************************************
  281.  * function: static void sequence_zones(unsigned char *image,int dx,int dy,int order)
  282.  *  Extract zones from buffer, place in zone_list, update zone_count and
  283.  *  sequence zones so they are in either COLUMN_MAJOR or ROW_MAJOR reading order.
  284.  * parameters: pointer to image buffer and x/y extents of image in buffer
  285.  *              order, COLUMN_MAJOR or ROW_MAJOR
  286.  * returns: nothing
  287.  ************************************************/
  288. static void sequence_zones(unsigned char *image,int dx,int dy,int order)
  289.     {
  290.     int x,y,i,j,index,fudge_x,fudge_y;
  291.     unsigned char *ptr;
  292.     ZONE temp;
  293.  
  294.     for (y=0, zone_count=0 ; y < dy && zone_count < MAX_ZONES ; y+=MIN_Y_ZONE)
  295.         {   /* extract zones from block images in y order */
  296.         ptr = image + y * dx;
  297.         for (x = 0 ; x < dx  ; x += MIN_X_ZONE)
  298.             if (*(ptr+x))                       /* found point */
  299.                 {
  300.                 if (zone_count >= MAX_ZONES)
  301.                     break;
  302.                 while (x > 0 && *(ptr+x-1))     /* back up to left side */
  303.                     x--;
  304.                 if (extract_zone(image,dx,dy,x,y,zone_list+zone_count))
  305.                     zone_count++;               /* get zone */
  306.                 }
  307.         }
  308.     if (zone_count == 0)
  309.         {           /* if no zones, make the entire page one big zone */
  310.         zone_list[0].x1 = zone_list[0].y1 = 0;
  311.         zone_list[0].x2 = dx-1;
  312.         zone_list[0].y2 = dy-1;
  313.         zone_count = 1;
  314.         return;
  315.         }
  316.     overlap_zones(zone_list,&zone_count);       /* remove overlap */
  317.     if (order == COLUMN_MAJOR)
  318.         {
  319.         for (i = 0 ; i < zone_count-1 ; i++)
  320.             {                                       /* sort on x1 */
  321.             for (j = i+1 ; j < zone_count ; j++)
  322.                 {
  323.                 if (zone_list[j].x1 < zone_list[i].x1)
  324.                     {
  325.                     temp = zone_list[i];
  326.                     zone_list[i] = zone_list[j];
  327.                     zone_list[j] = temp;
  328.                     }
  329.                 }
  330.             }
  331.         for (i = 0 ; i < zone_count-1 ; i++)
  332.             {           /* order zones left to right, up to down */
  333.             x = zone_list[i].x2;
  334.             y = zone_list[i].y1;
  335.             fudge_x = zone_list[i].x1+dx/20;    /* 5% slippage on alignment */
  336.             for (j = i+1, index = -1 ; j < zone_count ; j++)
  337.                 {   /* find any zones above and within x extent of zone_list[i] */
  338.                 if (zone_list[j].x1 > x)
  339.                     break;
  340.                 if (zone_list[j].y1 < y && zone_list[j].x1 <= fudge_x)
  341.                     {
  342.                     x = zone_list[j].x2;
  343.                     y = zone_list[j].y1;
  344.                     index = j;
  345.                     }
  346.                 }
  347.             if (index != -1)
  348.                 {
  349.                 temp = zone_list[i];
  350.                 zone_list[i] = zone_list[index];
  351.                 zone_list[index] = temp;
  352.                 }
  353.             }
  354.         }
  355.     else        /* ROW_MAJOR */
  356.         {   /* already sorted in y1 order */
  357.         for (i = 0 ; i < zone_count-1 ; i++)
  358.             {           /* order zones up to down, left to right */
  359.             y = zone_list[i].y2;
  360.             x = zone_list[i].x1;
  361.             fudge_y = zone_list[i].y1+dy/20;    /* 5% slippage on alignment */
  362.             for (j = i+1, index = -1 ; j < zone_count ; j++)
  363.                 {   /* find any zones left of and within y extent of zone_list[i] */
  364.                 if (zone_list[j].y1 > y)
  365.                     break;
  366.                 if (zone_list[j].x1 < x && zone_list[j].y1 <= fudge_y)
  367.                     {
  368.                     y = zone_list[j].y2;
  369.                     x = zone_list[j].x1;
  370.                     index = j;
  371.                     }
  372.                 }
  373.             if (index != -1)
  374.                 {
  375.                 temp = zone_list[i];
  376.                 zone_list[i] = zone_list[index];
  377.                 zone_list[index] = temp;
  378.                 }
  379.             }
  380.         }
  381.     }
  382.  
  383.  
  384. /************************************************
  385.  * function: static int extract_zone(unsigned char *image,int dx,int dy,int x,int y,ZONE *zone_ptr)
  386.  *  Extracts a rectangular region from the buffer starting at a point.  It does
  387.  *  this by wlaking the perimeter, recording the min and max x/y dimensions.
  388.  *  If the region is big enough to be significant, it is saved as a zone and then
  389.  *  whited out so it won't be reconsidered.
  390.  * parameters: pointer to image buffer and dx/dy extents of that buffer.
  391.  *             x & y point to start extracting the zone.
  392.  *             pointer to storage for zone
  393.  * returns: 1 if zone OK or 0 if not
  394.  ************************************************/
  395. static int extract_zone(unsigned char *image,int dx,int dy,int x,int y,ZONE *zone_ptr)
  396.     {
  397.     int ix,iy,minx,miny,maxx,maxy;  /* perimeter variables & min/max values */
  398.     HEADING dir;                    /* current direction */
  399.     unsigned char *previous,*next,*here;    /* buffer pointers */
  400.  
  401.     minx = maxx = ix = x;           /* preset min/max x/y and perimeter vars */
  402.     miny = maxy = iy = y;
  403.     dir  = GOING_UP;                /* starting direction */
  404.     do      /* walk perimeter, recording min/max of rectangular region */
  405.         {
  406.         if (ix < minx)              /* update min/max */
  407.             minx = ix;
  408.         if (ix > maxx)
  409.             maxx = ix;
  410.         if (iy < miny)
  411.             miny = iy;
  412.         if (iy > maxy)
  413.             maxy = iy;
  414.         here = image + iy * dx + ix;    /* where are we? */
  415.         next = here + dx;
  416.         previous = here - dx;
  417.         switch (dir)                    /* based on current direction, */
  418.             {                           /* look around for next direction */
  419.             case GOING_UP:
  420.             if (ix > 0 && *(here-1))
  421.                 {
  422.                 ix--;
  423.                 dir = GOING_LEFT;
  424.                 break;
  425.                 }
  426.             if (iy > 0 && *previous)
  427.                 {
  428.                 iy--;
  429.                 break;
  430.                 }
  431.             if (ix < dx-1 && *(here+1))
  432.                 {
  433.                 ix++;
  434.                 dir = GOING_RIGHT;
  435.                 break;
  436.                 }
  437.             if (iy < dy-1 && *next)
  438.                 {
  439.                 iy++;
  440.                 dir = GOING_DOWN;
  441.                 break;
  442.                 }
  443.             break;
  444.             case GOING_RIGHT:
  445.             if (iy > 0 && *previous)
  446.                 {
  447.                 iy--;
  448.                 dir = GOING_UP;
  449.                 break;
  450.                 }
  451.             if (ix < dx-1 && *(here+1))
  452.                 {
  453.                 ix++;
  454.                 break;
  455.                 }
  456.             if (iy < dy-1 && *next)
  457.                 {
  458.                 iy++;
  459.                 dir = GOING_DOWN;
  460.                 break;
  461.                 }
  462.             if (ix > 0 && *(here-1))
  463.                 {
  464.                 ix--;
  465.                 dir = GOING_LEFT;
  466.                 break;
  467.                 }
  468.             break;
  469.             case GOING_DOWN:
  470.             if (ix < dx-1 && *(here+1))
  471.                 {
  472.                 ix++;
  473.                 dir = GOING_RIGHT;
  474.                 break;
  475.                 }
  476.             if (iy < dy-1 && *next)
  477.                 {
  478.                 iy++;
  479.                 break;
  480.                 }
  481.             if (ix > 0 && *(here-1))
  482.                 {
  483.                 ix--;
  484.                 dir = GOING_LEFT;
  485.                 break;
  486.                 }
  487.             if (iy > 0 && *previous)
  488.                 {
  489.                 iy--;
  490.                 dir = GOING_UP;
  491.                 break;
  492.                 }
  493.             break;
  494.             case GOING_LEFT:
  495.             if (iy < dy-1 && *next)
  496.                 {
  497.                 iy++;
  498.                 dir = GOING_DOWN;
  499.                 break;
  500.                 }
  501.             if (ix > 0 && *(here-1))
  502.                 {
  503.                 ix--;
  504.                 break;
  505.                 }
  506.             if (iy > 0 && *previous)
  507.                 {
  508.                 iy--;
  509.                 dir = GOING_UP;
  510.                 break;
  511.                 }
  512.             if (ix < dx-1 && *(here+1))
  513.                 {
  514.                 ix++;
  515.                 dir = GOING_RIGHT;
  516.                 break;
  517.                 }
  518.             break;
  519.             }
  520.         } while (ix != x || iy != y);       /* until we return to the start */
  521.     for (iy=miny, here=image+miny*dx+minx ; iy <= maxy ; iy++, here+=dx)
  522.         memset(here,WHITE,maxx-minx+1);     /* whiteout the region */
  523.     if ((maxx-minx+1 < MIN_X_ZONE) || (maxy-miny+1 < MIN_Y_ZONE))
  524.         return 0;                           /* big enough? */
  525.     if (minx > 0)               /* expand dimensions by one pixel */
  526.         minx--;
  527.     if (maxx < dx-1)
  528.         maxx++;
  529.     if (miny > 0)
  530.         miny--;
  531.     if (maxy < dy-1)
  532.         maxy++;
  533.     zone_ptr->x1 = minx;        /* save zone */
  534.     zone_ptr->y1 = miny;
  535.     zone_ptr->x2 = maxx;
  536.     zone_ptr->y2 = maxy;
  537.     return 1;
  538.     }
  539.  
  540.  
  541. /************************************************
  542.  * function: static void overlap_zones(ZONE *zone_array,int *array_size)
  543.  *  find zones that overlap and combine them
  544.  * parameters: pointer to array of zones and pointer to count of zones
  545.  * returns: nothing
  546.  ************************************************/
  547. static void overlap_zones(ZONE *zone_array,int *array_size)
  548.     {
  549.     int i,j,ax1,ay1,ax2,ay2,bx1,by1,bx2,by2;
  550.  
  551.     for (i = 0 ; i < *array_size-1 ; i++)
  552.         {   /* compare each zone against the rest */
  553.         ax1 = (zone_array+i)->x1;
  554.         ay1 = (zone_array+i)->y1;
  555.         ax2 = (zone_array+i)->x2;
  556.         ay2 = (zone_array+i)->y2;
  557.         for (j = i+1 ; j < *array_size ; j++)
  558.             {
  559.             bx1 = (zone_array+j)->x1;
  560.             by1 = (zone_array+j)->y1;
  561.             bx2 = (zone_array+j)->x2;
  562.             by2 = (zone_array+j)->y2;
  563.             if ((bx1>=ax2) || (ax1>=bx2) || (by1>=ay2) || (ay1>=by2))
  564.                 continue;           /* no overlap */
  565.             bx1 = min(bx1,ax1);     /* combine zones */
  566.             by1 = min(by1,ay1);
  567.             bx2 = max(bx2,ax2);
  568.             by2 = max(by2,ay2);
  569.             (zone_array+i)->x1 = bx1;
  570.             (zone_array+i)->y1 = by1;
  571.             (zone_array+i)->x2 = bx2;
  572.             (zone_array+i)->y2 = by2;
  573.             (*array_size)--;        /* new zone count */
  574.             memmove((char *)(zone_array+j),(char *)(zone_array+j+1),
  575.                 sizeof(ZONE)*(*array_size-j));  /* shift array to remove zone */
  576.             i = -1;                 /* start all over */
  577.             break;
  578.             }
  579.         }
  580.     }
  581.  
  582.