1 /*******************************************************************************
2 
3 License:
4 This software was developed at the National Institute of Standards and
5 Technology (NIST) by employees of the Federal Government in the course
6 of their official duties. Pursuant to title 17 Section 105 of the
7 United States Code, this software is not subject to copyright protection
8 and is in the public domain. NIST assumes no responsibility  whatsoever for
9 its use by other parties, and makes no guarantees, expressed or implied,
10 about its quality, reliability, or any other characteristic.
11 
12 Disclaimer:
13 This software was developed to promote biometric standards and biometric
14 technology testing for the Federal Government in accordance with the USA
15 PATRIOT Act and the Enhanced Border Security and Visa Entry Reform Act.
16 Specific hardware and software products identified in this software were used
17 in order to perform the software development.  In no case does such
18 identification imply recommendation or endorsement by the National Institute
19 of Standards and Technology, nor does it imply that the products and equipment
20 identified are necessarily the best available for the purpose.
21 
22 *******************************************************************************/
23 
24 /***********************************************************************
25       LIBRARY: LFS - NIST Latent Fingerprint System
26 
27       FILE:    MAPS.C
28       AUTHOR:  Michael D. Garris
29       DATE:    03/16/1999
30       UPDATED: 10/04/1999 Version 2 by MDG
31       UPDATED: 10/26/1999 by MDG
32                To permit margin blocks to be flagged in
33                low contrast and low flow maps.
34       UPDATED: 03/16/2005 by MDG
35 
36       Contains routines responsible for computing various block-based
37       maps (including directional ridge flow maps) from an arbitrarily-
38       sized image as part of the NIST Latent Fingerprint System (LFS).
39 
40 ***********************************************************************
41                ROUTINES:
42                         gen_image_maps()
43                         gen_initial_maps()
44                         interpolate_direction_map()
45                         morph_TF_map()
46                         pixelize_map()
47                         smooth_direction_map()
48                         gen_high_curve_map()
49                         gen_initial_imap()
50                         primary_dir_test()
51                         secondary_fork_test()
52                         remove_incon_dirs()
53                         test_top_edge()
54                         test_right_edge()
55                         test_bottom_edge()
56                         test_left_edge()
57                         remove_dir()
58                         average_8nbr_dir()
59                         num_valid_8nbrs()
60                         smooth_imap()
61                         vorticity()
62                         accum_nbr_vorticity()
63                         curvature()
64 
65 ***********************************************************************/
66 
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <lfs.h>
71 #include <morph.h>
72 #include <log.h>
73 
74 /*************************************************************************
75 **************************************************************************
76 #cat: gen_image_maps - Computes a set of image maps based on Version 2
77 #cat:            of the NIST LFS System.  The first map is a Direction Map
78 #cat:            which is a 2D vector of integer directions, where each
79 #cat:            direction represents the dominant ridge flow in a block of
80 #cat:            the input grayscale image.  The Low Contrast Map flags
81 #cat:            blocks with insufficient contrast.  The Low Flow Map flags
82 #cat:            blocks with insufficient ridge flow.  The High Curve Map
83 #cat:            flags blocks containing high curvature. This routine will
84 #cat:            generate maps for an arbitrarily sized, non-square, image.
85 
86    Input:
87       pdata     - padded input image data (8 bits [0..256) grayscale)
88       pw        - padded width (in pixels) of the input image
89       ph        - padded height (in pixels) of the input image
90       dir2rad   - lookup table for converting integer directions
91       dftwaves  - structure containing the DFT wave forms
92       dftgrids  - structure containing the rotated pixel grid offsets
93       lfsparms  - parameters and thresholds for controlling LFS
94    Output:
95       odmap     - points to the created Direction Map
96       olcmap    - points to the created Low Contrast Map
97       olfmap    - points to the Low Ridge Flow Map
98       ohcmap    - points to the High Curvature Map
99       omw       - width (in blocks) of the maps
100       omh       - height (in blocks) of the maps
101    Return Code:
102       Zero     - successful completion
103       Negative - system error
104 **************************************************************************/
gen_image_maps(int ** odmap,int ** olcmap,int ** olfmap,int ** ohcmap,int * omw,int * omh,unsigned char * pdata,const int pw,const int ph,const DIR2RAD * dir2rad,const DFTWAVES * dftwaves,const ROTGRIDS * dftgrids,const LFSPARMS * lfsparms)105 int gen_image_maps(int **odmap, int **olcmap, int **olfmap, int **ohcmap,
106               int *omw, int *omh,
107               unsigned char *pdata, const int pw, const int ph,
108               const DIR2RAD *dir2rad, const DFTWAVES *dftwaves,
109               const ROTGRIDS *dftgrids, const LFSPARMS *lfsparms)
110 {
111    int *direction_map, *low_contrast_map, *low_flow_map, *high_curve_map;
112    int mw, mh, iw, ih;
113    int *blkoffs;
114    int ret; /* return code */
115 
116    /* 1. Compute block offsets for the entire image, accounting for pad */
117    /* Block_offsets() assumes square block (grid), so ERROR otherwise. */
118    if(dftgrids->grid_w != dftgrids->grid_h){
119       fprintf(stderr,
120               "ERROR : gen_image_maps : DFT grids must be square\n");
121       return(-540);
122    }
123    /* Compute unpadded image dimensions. */
124    iw = pw - (dftgrids->pad<<1);
125    ih = ph - (dftgrids->pad<<1);
126    if((ret = block_offsets(&blkoffs, &mw, &mh, iw, ih,
127                         dftgrids->pad, lfsparms->blocksize))){
128       return(ret);
129    }
130 
131    /* 2. Generate initial Direction Map and Low Contrast Map*/
132    if((ret = gen_initial_maps(&direction_map, &low_contrast_map,
133                               &low_flow_map, blkoffs, mw, mh,
134                               pdata, pw, ph, dftwaves, dftgrids, lfsparms))){
135       /* Free memory allocated to this point. */
136       free(blkoffs);
137       return(ret);
138    }
139 
140    if((ret = morph_TF_map(low_flow_map, mw, mh, lfsparms))){
141       return(ret);
142    }
143 
144    /* 3. Remove directions that are inconsistent with neighbors */
145    remove_incon_dirs(direction_map, mw, mh, dir2rad, lfsparms);
146 
147 
148    /* 4. Smooth Direction Map values with their neighbors */
149    smooth_direction_map(direction_map, low_contrast_map, mw, mh,
150                            dir2rad, lfsparms);
151 
152    /* 5. Interpolate INVALID direction blocks with their valid neighbors. */
153    if((ret = interpolate_direction_map(direction_map, low_contrast_map,
154                                        mw, mh, lfsparms))){
155       return(ret);
156    }
157 
158    /* May be able to skip steps 6 and/or 7 if computation time */
159    /* is a critical factor.                                    */
160 
161    /* 6. Remove directions that are inconsistent with neighbors */
162    remove_incon_dirs(direction_map, mw, mh, dir2rad, lfsparms);
163 
164    /* 7. Smooth Direction Map values with their neighbors. */
165    smooth_direction_map(direction_map, low_contrast_map, mw, mh,
166                            dir2rad, lfsparms);
167 
168    /* 8. Set the Direction Map values in the image margin to INVALID. */
169    set_margin_blocks(direction_map, mw, mh, INVALID_DIR);
170 
171    /* 9. Generate High Curvature Map from interpolated Direction Map. */
172    if((ret = gen_high_curve_map(&high_curve_map, direction_map, mw, mh,
173                                 lfsparms))){
174       return(ret);
175    }
176 
177    /* Deallocate working memory. */
178    free(blkoffs);
179 
180    *odmap = direction_map;
181    *olcmap = low_contrast_map;
182    *olfmap = low_flow_map;
183    *ohcmap = high_curve_map;
184    *omw = mw;
185    *omh = mh;
186    return(0);
187 }
188 
189 /*************************************************************************
190 **************************************************************************
191 #cat: gen_initial_maps - Creates an initial Direction Map from the given
192 #cat:             input image.  It very important that the image be properly
193 #cat:             padded so that rotated grids along the boundary of the image
194 #cat:             do not access unkown memory.  The rotated grids are used by a
195 #cat:             DFT-based analysis to determine the integer directions
196 #cat:             in the map. Typically this initial vector of directions will
197 #cat:             subsequently have weak or inconsistent directions removed
198 #cat:             followed by a smoothing process.  The resulting Direction
199 #cat:             Map contains valid directions >= 0 and INVALID values = -1.
200 #cat:             This routine also computes and returns 2 other image maps.
201 #cat:             The Low Contrast Map flags blocks in the image with
202 #cat:             insufficient contrast.  Blocks with low contrast have a
203 #cat:             corresponding direction of INVALID in the Direction Map.
204 #cat:             The Low Flow Map flags blocks in which the DFT analyses
205 #cat:             could not determine a significant ridge flow.  Blocks with
206 #cat:             low ridge flow also have a corresponding direction of
207 #cat:             INVALID in the Direction Map.
208 
209    Input:
210       blkoffs   - offsets to the pixel origin of each block in the padded image
211       mw        - number of blocks horizontally in the padded input image
212       mh        - number of blocks vertically in the padded input image
213       pdata     - padded input image data (8 bits [0..256) grayscale)
214       pw        - width (in pixels) of the padded input image
215       ph        - height (in pixels) of the padded input image
216       dftwaves  - structure containing the DFT wave forms
217       dftgrids  - structure containing the rotated pixel grid offsets
218       lfsparms  - parameters and thresholds for controlling LFS
219    Output:
220       odmap     - points to the newly created Direction Map
221       olcmap    - points to the newly created Low Contrast Map
222    Return Code:
223       Zero     - successful completion
224       Negative - system error
225 **************************************************************************/
gen_initial_maps(int ** odmap,int ** olcmap,int ** olfmap,int * blkoffs,const int mw,const int mh,unsigned char * pdata,const int pw,const int ph,const DFTWAVES * dftwaves,const ROTGRIDS * dftgrids,const LFSPARMS * lfsparms)226 int gen_initial_maps(int **odmap, int **olcmap, int **olfmap,
227                 int *blkoffs, const int mw, const int mh,
228                 unsigned char *pdata, const int pw, const int ph,
229                 const DFTWAVES *dftwaves, const  ROTGRIDS *dftgrids,
230                 const LFSPARMS *lfsparms)
231 {
232    int *direction_map, *low_contrast_map, *low_flow_map;
233    int bi, bsize, blkdir;
234    int *wis, *powmax_dirs;
235    double **powers, *powmaxs, *pownorms;
236    int nstats;
237    int ret; /* return code */
238    int dft_offset;
239    int xminlimit, xmaxlimit, yminlimit, ymaxlimit;
240    int win_x, win_y, low_contrast_offset;
241 
242    print2log("INITIAL MAP\n");
243 
244    /* Compute total number of blocks in map */
245    bsize = mw * mh;
246 
247    /* Allocate Direction Map memory */
248    direction_map = (int *)malloc(bsize * sizeof(int));
249    if(direction_map == (int *)NULL){
250       fprintf(stderr,
251               "ERROR : gen_initial_maps : malloc : direction_map\n");
252       return(-550);
253    }
254    /* Initialize the Direction Map to INVALID (-1). */
255    memset(direction_map, INVALID_DIR, bsize * sizeof(int));
256 
257    /* Allocate Low Contrast Map memory */
258    low_contrast_map = (int *)malloc(bsize * sizeof(int));
259    if(low_contrast_map == (int *)NULL){
260       free(direction_map);
261       fprintf(stderr,
262               "ERROR : gen_initial_maps : malloc : low_contrast_map\n");
263       return(-551);
264    }
265    /* Initialize the Low Contrast Map to FALSE (0). */
266    memset(low_contrast_map, 0, bsize * sizeof(int));
267 
268    /* Allocate Low Ridge Flow Map memory */
269    low_flow_map = (int *)malloc(bsize * sizeof(int));
270    if(low_flow_map == (int *)NULL){
271       free(direction_map);
272       free(low_contrast_map);
273       fprintf(stderr,
274               "ERROR : gen_initial_maps : malloc : low_flow_map\n");
275       return(-552);
276    }
277    /* Initialize the Low Flow Map to FALSE (0). */
278    memset(low_flow_map, 0, bsize * sizeof(int));
279 
280    /* Allocate DFT directional power vectors */
281    if((ret = alloc_dir_powers(&powers, dftwaves->nwaves, dftgrids->ngrids))){
282       /* Free memory allocated to this point. */
283       free(direction_map);
284       free(low_contrast_map);
285       free(low_flow_map);
286       return(ret);
287    }
288 
289    /* Allocate DFT power statistic arrays */
290    /* Compute length of statistics arrays.  Statistics not needed   */
291    /* for the first DFT wave, so the length is number of waves - 1. */
292    nstats = dftwaves->nwaves - 1;
293    if((ret = alloc_power_stats(&wis, &powmaxs, &powmax_dirs,
294                             &pownorms, nstats))){
295       /* Free memory allocated to this point. */
296       free(direction_map);
297       free(low_contrast_map);
298       free(low_flow_map);
299       free_dir_powers(powers, dftwaves->nwaves);
300       return(ret);
301    }
302 
303    /* Compute special window origin limits for determining low contrast.  */
304    /* These pixel limits avoid analyzing the padded borders of the image. */
305    xminlimit = dftgrids->pad;
306    yminlimit = dftgrids->pad;
307    xmaxlimit = pw - dftgrids->pad - lfsparms->windowsize - 1;
308    ymaxlimit = ph - dftgrids->pad - lfsparms->windowsize - 1;
309 
310    /* max limits should not be negative */
311    xmaxlimit = MAX(xmaxlimit, 0);
312    ymaxlimit = MAX(ymaxlimit, 0);
313 
314    /* Foreach block in image ... */
315    for(bi = 0; bi < bsize; bi++){
316       /* Adjust block offset from pointing to block origin to pointing */
317       /* to surrounding window origin.                                 */
318       dft_offset = blkoffs[bi] - (lfsparms->windowoffset * pw) -
319                       lfsparms->windowoffset;
320 
321       /* Compute pixel coords of window origin. */
322       win_x = dft_offset % pw;
323       win_y = (int)(dft_offset / pw);
324 
325       /* Make sure the current window does not access padded image pixels */
326       /* for analyzing low contrast.                                      */
327       win_x = max(xminlimit, win_x);
328       win_x = min(xmaxlimit, win_x);
329       win_y = max(yminlimit, win_y);
330       win_y = min(ymaxlimit, win_y);
331       low_contrast_offset = (win_y * pw) + win_x;
332 
333       print2log("   BLOCK %2d (%2d, %2d) ", bi, bi%mw, bi/mw);
334 
335       /* If block is low contrast ... */
336       if((ret = low_contrast_block(low_contrast_offset, lfsparms->windowsize,
337                                   pdata, pw, ph, lfsparms))){
338          /* If system error ... */
339          if(ret < 0){
340             free(direction_map);
341             free(low_contrast_map);
342             free(low_flow_map);
343             free_dir_powers(powers, dftwaves->nwaves);
344             free(wis);
345             free(powmaxs);
346             free(powmax_dirs);
347             free(pownorms);
348             return(ret);
349          }
350 
351          /* Otherwise, block is low contrast ... */
352          print2log("LOW CONTRAST\n");
353          low_contrast_map[bi] = TRUE;
354          /* Direction Map's block is already set to INVALID. */
355       }
356       /* Otherwise, sufficient contrast for DFT processing ... */
357       else {
358          print2log("\n");
359 
360          /* Compute DFT powers */
361          if((ret = dft_dir_powers(powers, pdata, low_contrast_offset, pw, ph,
362                                dftwaves, dftgrids))){
363             /* Free memory allocated to this point. */
364             free(direction_map);
365             free(low_contrast_map);
366             free(low_flow_map);
367             free_dir_powers(powers, dftwaves->nwaves);
368             free(wis);
369             free(powmaxs);
370             free(powmax_dirs);
371             free(pownorms);
372             return(ret);
373          }
374 
375          /* Compute DFT power statistics, skipping first applied DFT  */
376          /* wave.  This is dependent on how the primary and secondary */
377          /* direction tests work below.                               */
378          if((ret = dft_power_stats(wis, powmaxs, powmax_dirs, pownorms, powers,
379                                 1, dftwaves->nwaves, dftgrids->ngrids))){
380             /* Free memory allocated to this point. */
381             free(direction_map);
382             free(low_contrast_map);
383             free(low_flow_map);
384             free_dir_powers(powers, dftwaves->nwaves);
385             free(wis);
386             free(powmaxs);
387             free(powmax_dirs);
388             free(pownorms);
389             return(ret);
390          }
391 
392 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
393          {  int _w;
394             fprintf(logfp, "      Power\n");
395             for(_w = 0; _w < nstats; _w++){
396                /* Add 1 to wis[w] to create index to original dft_coefs[] */
397                fprintf(logfp, "         wis[%d] %d %12.3f %2d %9.3f %12.3f\n",
398                     _w, wis[_w]+1,
399                     powmaxs[wis[_w]], powmax_dirs[wis[_w]], pownorms[wis[_w]],
400                     powers[0][powmax_dirs[wis[_w]]]);
401             }
402          }
403 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
404 
405          /* Conduct primary direction test */
406          blkdir = primary_dir_test(powers, wis, powmaxs, powmax_dirs,
407                                   pownorms, nstats, lfsparms);
408 
409          if(blkdir != INVALID_DIR)
410             direction_map[bi] = blkdir;
411          else{
412             /* Conduct secondary (fork) direction test */
413             blkdir = secondary_fork_test(powers, wis, powmaxs, powmax_dirs,
414                                   pownorms, nstats, lfsparms);
415             if(blkdir != INVALID_DIR)
416                direction_map[bi] = blkdir;
417             /* Otherwise current direction in Direction Map remains INVALID */
418             else
419                /* Flag the block as having LOW RIDGE FLOW. */
420                low_flow_map[bi] = TRUE;
421          }
422 
423       } /* End DFT */
424    } /* bi */
425 
426    /* Deallocate working memory */
427    free_dir_powers(powers, dftwaves->nwaves);
428    free(wis);
429    free(powmaxs);
430    free(powmax_dirs);
431    free(pownorms);
432 
433    *odmap = direction_map;
434    *olcmap = low_contrast_map;
435    *olfmap = low_flow_map;
436    return(0);
437 }
438 
439 /*************************************************************************
440 **************************************************************************
441 #cat: interpolate_direction_map - Take a Direction Map and Low Contrast
442 #cat:             Map and attempts to fill in INVALID directions in the
443 #cat:             Direction Map based on a blocks valid neighbors.  The
444 #cat:             valid neighboring directions are combined in a weighted
445 #cat:             average inversely proportional to their distance from
446 #cat:             the block being interpolated.  Low Contrast blocks are
447 #cat:             used to prempt the search for a valid neighbor in a
448 #cat:             specific direction, which keeps the process from
449 #cat:             interpolating directions for blocks in the background and
450 #cat:             and perimeter of the fingerprint in the image.
451 
452    Input:
453       direction_map    - map of blocks containing directional ridge flow
454       low_contrast_map - map of blocks flagged as LOW CONTRAST
455       mw        - number of blocks horizontally in the maps
456       mh        - number of blocks vertically in the maps
457       lfsparms  - parameters and thresholds for controlling LFS
458    Output:
459       direction_map - contains the newly interpolated results
460    Return Code:
461       Zero     - successful completion
462       Negative - system error
463 **************************************************************************/
interpolate_direction_map(int * direction_map,int * low_contrast_map,const int mw,const int mh,const LFSPARMS * lfsparms)464 int interpolate_direction_map(int *direction_map, int *low_contrast_map,
465                       const int mw, const int mh, const LFSPARMS *lfsparms)
466 {
467    int x, y, new_dir;
468    int n_dir, e_dir, s_dir, w_dir;
469    int n_dist = 0, e_dist = 0, s_dist = 0, w_dist = 0, total_dist;
470    int n_found, e_found, s_found, w_found, total_found;
471    int n_delta = 0, e_delta = 0, s_delta = 0, w_delta = 0, total_delta;
472    int nbr_x, nbr_y;
473    int *omap, *dptr, *cptr, *optr;
474    double avr_dir;
475 
476    print2log("INTERPOLATE DIRECTION MAP\n");
477 
478    /* Allocate output (interpolated) Direction Map. */
479    omap = (int *)malloc(mw*mh*sizeof(int));
480    if(omap == (int *)NULL){
481       fprintf(stderr,
482               "ERROR : interpolate_direction_map : malloc : omap\n");
483       return(-520);
484    }
485 
486    /* Set pointers to the first block in the maps. */
487    dptr = direction_map;
488    cptr = low_contrast_map;
489    optr = omap;
490 
491    /* Foreach block in the maps ... */
492    for(y = 0; y < mh; y++){
493       for(x = 0; x < mw; x++){
494 
495          /* If image block is NOT LOW CONTRAST and has INVALID direction ... */
496          if((!*cptr) && (*dptr == INVALID_DIR)){
497 
498             /* Set neighbor accumulators to 0. */
499             total_found = 0;
500             total_dist = 0;
501 
502             /* Find north neighbor. */
503             if((n_found = find_valid_block(&n_dir, &nbr_x, &nbr_y,
504                                             direction_map, low_contrast_map,
505                                             x, y, mw, mh, 0, -1)) == FOUND){
506                /* Compute north distance. */
507                n_dist = y - nbr_y;
508                /* Accumulate neighbor distance. */
509                total_dist += n_dist;
510                /* Bump number of neighbors found. */
511                total_found++;
512             }
513 
514             /* Find east neighbor. */
515             if((e_found = find_valid_block(&e_dir, &nbr_x, &nbr_y,
516                                             direction_map, low_contrast_map,
517                                             x, y, mw, mh, 1, 0)) == FOUND){
518                /* Compute east distance. */
519                e_dist = nbr_x - x;
520                /* Accumulate neighbor distance. */
521                total_dist += e_dist;
522                /* Bump number of neighbors found. */
523                total_found++;
524             }
525 
526             /* Find south neighbor. */
527             if((s_found = find_valid_block(&s_dir, &nbr_x, &nbr_y,
528                                             direction_map, low_contrast_map,
529                                             x, y, mw, mh, 0, 1)) == FOUND){
530                /* Compute south distance. */
531                s_dist = nbr_y - y;
532                /* Accumulate neighbor distance. */
533                total_dist += s_dist;
534                /* Bump number of neighbors found. */
535                total_found++;
536             }
537 
538             /* Find west neighbor. */
539             if((w_found = find_valid_block(&w_dir, &nbr_x, &nbr_y,
540                                             direction_map, low_contrast_map,
541                                             x, y, mw, mh, -1, 0)) == FOUND){
542                /* Compute west distance. */
543                w_dist = x - nbr_x;
544                /* Accumulate neighbor distance. */
545                total_dist += w_dist;
546                /* Bump number of neighbors found. */
547                total_found++;
548             }
549 
550             /* If a sufficient number of neighbors found (Ex. 2) ... */
551             if(total_found >= lfsparms->min_interpolate_nbrs){
552 
553                /* Accumulate weighted sum of neighboring directions     */
554                /* inversely related to the distance from current block. */
555                total_delta = 0.0;
556                /* If neighbor found to the north ... */
557                if(n_found){
558                   n_delta = total_dist - n_dist;
559                   total_delta += n_delta;
560                }
561                /* If neighbor found to the east ... */
562                if(e_found){
563                   e_delta = total_dist - e_dist;
564                   total_delta += e_delta;
565                }
566                /* If neighbor found to the south ... */
567                if(s_found){
568                   s_delta = total_dist - s_dist;
569                   total_delta += s_delta;
570                }
571                /* If neighbor found to the west ... */
572                if(w_found){
573                   w_delta = total_dist - w_dist;
574                   total_delta += w_delta;
575                }
576 
577                avr_dir = 0.0;
578 
579                if(n_found){
580                   avr_dir += (n_dir*(n_delta/(double)total_delta));
581                }
582                if(e_found){
583                   avr_dir += (e_dir*(e_delta/(double)total_delta));
584                }
585                if(s_found){
586                   avr_dir += (s_dir*(s_delta/(double)total_delta));
587                }
588                if(w_found){
589                   avr_dir += (w_dir*(w_delta/(double)total_delta));
590                }
591 
592                /* Need to truncate precision so that answers are consistent  */
593                /* on different computer architectures when rounding doubles. */
594                avr_dir = trunc_dbl_precision(avr_dir, TRUNC_SCALE);
595 
596                /* Assign interpolated direction to output Direction Map. */
597                new_dir = sround(avr_dir);
598 
599                print2log("   Block %d,%d INTERP numnbs=%d newdir=%d\n",
600                        x, y, total_found, new_dir);
601 
602                *optr = new_dir;
603             }
604             else{
605                /* Otherwise, the direction remains INVALID. */
606                *optr = *dptr;
607             }
608          }
609          else{
610             /* Otherwise, assign the current direction to the output block. */
611             *optr = *dptr;
612          }
613 
614          /* Bump to the next block in the maps ... */
615          dptr++;
616          cptr++;
617          optr++;
618       }
619    }
620 
621    /* Copy the interpolated directions into the input map. */
622    memcpy(direction_map, omap, mw*mh*sizeof(int));
623    /* Deallocate the working memory. */
624    free(omap);
625 
626    /* Return normally. */
627    return(0);
628 }
629 
630 /*************************************************************************
631 **************************************************************************
632 #cat: morph_tf_map - Takes a 2D vector of TRUE and FALSE values integers
633 #cat:               and dialates and erodes the map in an attempt to fill
634 #cat:               in voids in the map.
635 
636    Input:
637       tfmap    - vector of integer block values
638       mw       - width (in blocks) of the map
639       mh       - height (in blocks) of the map
640       lfsparms - parameters and thresholds for controlling LFS
641    Output:
642       tfmap    - resulting morphed map
643 **************************************************************************/
morph_TF_map(int * tfmap,const int mw,const int mh,const LFSPARMS * lfsparms)644 int morph_TF_map(int *tfmap, const int mw, const int mh,
645                  const LFSPARMS *lfsparms)
646 {
647    unsigned char *cimage, *mimage, *cptr;
648    int *mptr;
649    int i;
650 
651 
652    /* Convert TRUE/FALSE map into a binary byte image. */
653    cimage = (unsigned char *)malloc(mw*mh);
654    if(cimage == (unsigned char *)NULL){
655       fprintf(stderr, "ERROR : morph_TF_map : malloc : cimage\n");
656       return(-660);
657    }
658 
659    mimage = (unsigned char *)malloc(mw*mh);
660    if(mimage == (unsigned char *)NULL){
661       fprintf(stderr, "ERROR : morph_TF_map : malloc : mimage\n");
662       return(-661);
663    }
664 
665    cptr = cimage;
666    mptr = tfmap;
667    for(i = 0; i < mw*mh; i++){
668       *cptr++ = *mptr++;
669    }
670 
671    dilate_charimage_2(cimage, mimage, mw, mh);
672    dilate_charimage_2(mimage, cimage, mw, mh);
673    erode_charimage_2(cimage, mimage, mw, mh);
674    erode_charimage_2(mimage, cimage, mw, mh);
675 
676    cptr = cimage;
677    mptr = tfmap;
678    for(i = 0; i < mw*mh; i++){
679       *mptr++ = *cptr++;
680    }
681 
682    free(cimage);
683    free(mimage);
684 
685    return(0);
686 }
687 
688 /*************************************************************************
689 **************************************************************************
690 #cat: pixelize_map - Takes a block image map and assigns each pixel in the
691 #cat:            image its corresponding block value.  This allows block
692 #cat:            values in maps to be directly accessed via pixel addresses.
693 
694    Input:
695       iw        - the width (in pixels) of the corresponding image
696       ih        - the height (in pixels) of the corresponding image
697       imap      - input block image map
698       mw        - the width (in blocks) of the map
699       mh        - the height (in blocks) of the map
700       blocksize - the dimension (in pixels) of each block
701    Output:
702       omap      - points to the resulting pixelized map
703    Return Code:
704       Zero     - successful completion
705       Negative - system error
706 **************************************************************************/
pixelize_map(int ** omap,const int iw,const int ih,int * imap,const int mw,const int mh,const int blocksize)707 int pixelize_map(int **omap, const int iw, const int ih,
708                   int *imap, const int mw, const int mh, const int blocksize)
709 {
710    int *pmap;
711    int ret, x, y;
712    int *blkoffs, bw, bh, bi;
713    int *spptr, *pptr;
714 
715    pmap = (int *)malloc(iw*ih*sizeof(int));
716    if(pmap == (int *)NULL){
717       fprintf(stderr, "ERROR : pixelize_map : malloc : pmap\n");
718       return(-590);
719    }
720 
721    if((ret = block_offsets(&blkoffs, &bw, &bh, iw, ih, 0, blocksize))){
722       return(ret);
723    }
724 
725    if((bw != mw) || (bh != mh)){
726       free(blkoffs);
727       fprintf(stderr,
728          "ERROR : pixelize_map : block dimensions do not match\n");
729       return(-591);
730    }
731 
732    for(bi = 0; bi < mw*mh; bi++){
733       spptr = pmap + blkoffs[bi];
734       for(y = 0; y < blocksize; y++){
735          pptr = spptr;
736          for(x = 0; x < blocksize; x++){
737             *pptr++ = imap[bi];
738          }
739          spptr += iw;
740       }
741    }
742 
743    /* Deallocate working memory. */
744    free(blkoffs);
745    /* Assign pixelized map to output pointer. */
746    *omap = pmap;
747 
748    /* Return normally. */
749    return(0);
750 }
751 
752 /*************************************************************************
753 **************************************************************************
754 #cat: smooth_direction_map - Takes a vector of integer directions and smooths
755 #cat:               them by analyzing the direction of adjacent neighbors.
756 
757    Input:
758       direction_map - vector of integer block values
759       mw        - width (in blocks) of the map
760       mh        - height (in blocks) of the map
761       dir2rad   - lookup table for converting integer directions
762       lfsparms - parameters and thresholds for controlling LFS
763    Output:
764       imap      - vector of smoothed input values
765 **************************************************************************/
smooth_direction_map(int * direction_map,int * low_contrast_map,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)766 void smooth_direction_map(int *direction_map, int *low_contrast_map,
767                  const int mw, const int mh,
768                  const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
769 {
770    int mx, my;
771    int *dptr, *cptr;
772    int avrdir, nvalid;
773    double dir_strength;
774 
775    print2log("SMOOTH DIRECTION MAP\n");
776 
777    /* Assign pointers to beginning of both maps. */
778    dptr = direction_map;
779    cptr = low_contrast_map;
780 
781    /* Foreach block in maps ... */
782    for(my = 0; my < mh; my++){
783       for(mx = 0; mx < mw; mx++){
784          /* If the current block does NOT have LOW CONTRAST ... */
785          if(!*cptr){
786 
787             /* Compute average direction from neighbors, returning the */
788             /* number of valid neighbors used in the computation, and  */
789             /* the "strength" of the average direction.                */
790             average_8nbr_dir(&avrdir, &dir_strength, &nvalid,
791                              direction_map, mx, my, mw, mh, dir2rad);
792 
793             /* If average direction strength is strong enough */
794             /*    (Ex. thresh==0.2)...                        */
795             if(dir_strength >= lfsparms->dir_strength_min){
796                /* If Direction Map direction is valid ... */
797                if(*dptr != INVALID_DIR){
798                   /* Conduct valid neighbor test (Ex. thresh==3)... */
799                   if(nvalid >= lfsparms->rmv_valid_nbr_min){
800 
801 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
802                      fprintf(logfp, "   BLOCK %2d (%2d, %2d)\n",
803                              mx+(my*mw), mx, my);
804                      fprintf(logfp, "      Average NBR :   %2d %6.3f %d\n",
805                              avrdir, dir_strength, nvalid);
806                      fprintf(logfp, "      1. Valid NBR (%d >= %d)\n",
807                              nvalid, lfsparms->rmv_valid_nbr_min);
808                      fprintf(logfp, "      Valid Direction = %d\n", *dptr);
809                      fprintf(logfp, "      Smoothed Direction = %d\n", avrdir);
810 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
811 
812                      /* Reassign valid direction with average direction. */
813                      *dptr = avrdir;
814                   }
815                }
816                /* Otherwise direction is invalid ... */
817                else{
818                   /* Even if DIRECTION_MAP value is invalid, if number of */
819                   /* valid neighbors is big enough (Ex. thresh==7)...     */
820                   if(nvalid >= lfsparms->smth_valid_nbr_min){
821 
822 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
823                      fprintf(logfp, "   BLOCK %2d (%2d, %2d)\n",
824                              mx+(my*mw), mx, my);
825                      fprintf(logfp, "      Average NBR :   %2d %6.3f %d\n",
826                              avrdir, dir_strength, nvalid);
827                      fprintf(logfp, "      2. Invalid NBR (%d >= %d)\n",
828                              nvalid, lfsparms->smth_valid_nbr_min);
829                      fprintf(logfp, "      Invalid Direction = %d\n", *dptr);
830                      fprintf(logfp, "      Smoothed Direction = %d\n", avrdir);
831 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
832 
833                      /* Assign invalid direction with average direction. */
834                      *dptr = avrdir;
835                   }
836                }
837             }
838          }
839          /* Otherwise, block has LOW CONTRAST, so keep INVALID direction. */
840 
841          /* Bump to next block in maps. */
842          dptr++;
843          cptr++;
844       }
845    }
846 }
847 
848 /*************************************************************************
849 **************************************************************************
850 #cat: gen_high_curve_map - Takes a Direction Map and generates a new map
851 #cat:            that flags blocks with HIGH CURVATURE.
852 
853    Input:
854       direction_map - map of blocks containing directional ridge flow
855       mw        - the width (in blocks) of the map
856       mh        - the height (in blocks) of the map
857       lfsparms  - parameters and thresholds for controlling LFS
858    Output:
859       ohcmap    - points to the created High Curvature Map
860    Return Code:
861       Zero     - successful completion
862       Negative - system error
863 **************************************************************************/
gen_high_curve_map(int ** ohcmap,int * direction_map,const int mw,const int mh,const LFSPARMS * lfsparms)864 int gen_high_curve_map(int **ohcmap, int *direction_map,
865                    const int mw, const int mh, const LFSPARMS *lfsparms)
866 {
867    int *high_curve_map, mapsize;
868    int *hptr, *dptr;
869    int bx, by;
870    int nvalid, cmeasure, vmeasure;
871 
872    mapsize = mw*mh;
873 
874    /* Allocate High Curvature Map. */
875    high_curve_map = (int *)malloc(mapsize * sizeof(int));
876    if(high_curve_map == (int *)NULL){
877       fprintf(stderr,
878               "ERROR: gen_high_curve_map : malloc : high_curve_map\n");
879       return(-530);
880    }
881    /* Initialize High Curvature Map to FALSE (0). */
882    memset(high_curve_map, 0, mapsize*sizeof(int));
883 
884    hptr = high_curve_map;
885    dptr = direction_map;
886 
887    /* Foreach row in maps ... */
888    for(by = 0; by < mh; by++){
889       /* Foreach column in maps ... */
890       for(bx = 0; bx < mw; bx++){
891 
892          /* Count number of valid neighbors around current block ... */
893          nvalid = num_valid_8nbrs(direction_map, bx, by, mw, mh);
894 
895          /* If valid neighbors exist ... */
896          if(nvalid > 0){
897             /* If current block's direction is INVALID ... */
898             if(*dptr == INVALID_DIR){
899                /* If a sufficient number of VALID neighbors exists ... */
900                if(nvalid >= lfsparms->vort_valid_nbr_min){
901                   /* Measure vorticity of neighbors. */
902                   vmeasure = vorticity(direction_map, bx, by, mw, mh,
903                                        lfsparms->num_directions);
904                   /* If vorticity is sufficiently high ... */
905                   if(vmeasure >= lfsparms->highcurv_vorticity_min)
906                      /* Flag block as HIGH CURVATURE. */
907                      *hptr = TRUE;
908                }
909             }
910             /* Otherwise block has valid direction ... */
911             else{
912                /* Measure curvature around the valid block. */
913                cmeasure = curvature(direction_map, bx, by, mw, mh,
914                                     lfsparms->num_directions);
915                /* If curvature is sufficiently high ... */
916                if(cmeasure >= lfsparms->highcurv_curvature_min)
917                   *hptr = TRUE;
918             }
919          } /* Else (nvalid <= 0) */
920 
921          /* Bump pointers to next block in maps. */
922          dptr++;
923          hptr++;
924 
925       } /* bx */
926    } /* by */
927 
928    /* Assign High Curvature Map to output pointer. */
929    *ohcmap = high_curve_map;
930 
931    /* Return normally. */
932    return(0);
933 }
934 
935 /*************************************************************************
936 **************************************************************************
937 #cat: gen_initial_imap - Creates an initial IMAP from the given input image.
938 #cat:             It very important that the image be properly padded so
939 #cat:             that rotated grids along the boudary of the image do not
940 #cat:             access unkown memory.  The rotated grids are used by a
941 #cat:             DFT-based analysis to determine the integer directions
942 #cat:             in the IMAP. Typically this initial vector of directions will
943 #cat:             subsequently have weak or inconsistent directions removed
944 #cat:             followed by a smoothing process.
945 
946    Input:
947       blkoffs   - offsets to the pixel origin of each block in the padded image
948       mw        - number of blocks horizontally in the padded input image
949       mh        - number of blocks vertically in the padded input image
950       pdata     - padded input image data (8 bits [0..256) grayscale)
951       pw        - width (in pixels) of the padded input image
952       ph        - height (in pixels) of the padded input image
953       dftwaves  - structure containing the DFT wave forms
954       dftgrids  - structure containing the rotated pixel grid offsets
955       lfsparms  - parameters and thresholds for controlling LFS
956    Output:
957       optr      - points to the newly created IMAP
958    Return Code:
959       Zero     - successful completion
960       Negative - system error
961 **************************************************************************/
gen_initial_imap(int ** optr,int * blkoffs,const int mw,const int mh,unsigned char * pdata,const int pw,const int ph,const DFTWAVES * dftwaves,const ROTGRIDS * dftgrids,const LFSPARMS * lfsparms)962 int gen_initial_imap(int **optr, int *blkoffs, const int mw, const int mh,
963                 unsigned char *pdata, const int pw, const int ph,
964                 const DFTWAVES *dftwaves, const  ROTGRIDS *dftgrids,
965                 const LFSPARMS *lfsparms)
966 {
967    int *imap;
968    int bi, bsize, blkdir;
969    int *wis, *powmax_dirs;
970    double **powers, *powmaxs, *pownorms;
971    int nstats;
972    int ret; /* return code */
973 
974    print2log("INITIAL MAP\n");
975 
976    /* Compute total number of blocks in IMAP */
977    bsize = mw * mh;
978 
979    /* Allocate IMAP memory */
980    imap = (int *)malloc(bsize * sizeof(int));
981    if(imap == (int *)NULL){
982       fprintf(stderr, "ERROR : gen_initial_imap : malloc : imap\n");
983       return(-70);
984    }
985 
986    /* Allocate DFT directional power vectors */
987    if((ret = alloc_dir_powers(&powers, dftwaves->nwaves, dftgrids->ngrids))){
988       /* Free memory allocated to this point. */
989       free(imap);
990       return(ret);
991    }
992 
993    /* Allocate DFT power statistic arrays */
994    /* Compute length of statistics arrays.  Statistics not needed   */
995    /* for the first DFT wave, so the length is number of waves - 1. */
996    nstats = dftwaves->nwaves - 1;
997    if((ret = alloc_power_stats(&wis, &powmaxs, &powmax_dirs,
998                             &pownorms, nstats))){
999       /* Free memory allocated to this point. */
1000       free(imap);
1001       free_dir_powers(powers, dftwaves->nwaves);
1002       return(ret);
1003    }
1004 
1005    /* Initialize the imap to -1 */
1006    memset(imap, INVALID_DIR, bsize * sizeof(int));
1007 
1008    /* Foreach block in imap ... */
1009    for(bi = 0; bi < bsize; bi++){
1010 
1011       print2log("   BLOCK %2d (%2d, %2d)\n", bi, bi%mw, bi/mw);
1012 
1013       /* Compute DFT powers */
1014       if((ret = dft_dir_powers(powers, pdata, blkoffs[bi], pw, ph,
1015                             dftwaves, dftgrids))){
1016          /* Free memory allocated to this point. */
1017          free(imap);
1018          free_dir_powers(powers, dftwaves->nwaves);
1019          free(wis);
1020          free(powmaxs);
1021          free(powmax_dirs);
1022          free(pownorms);
1023          return(ret);
1024       }
1025 
1026       /* Compute DFT power statistics, skipping first applied DFT  */
1027       /* wave.  This is dependent on how the primary and secondary */
1028       /* direction tests work below.                               */
1029       if((ret = dft_power_stats(wis, powmaxs, powmax_dirs, pownorms, powers,
1030                       1, dftwaves->nwaves, dftgrids->ngrids))){
1031          /* Free memory allocated to this point. */
1032          free(imap);
1033          free_dir_powers(powers, dftwaves->nwaves);
1034          free(wis);
1035          free(powmaxs);
1036          free(powmax_dirs);
1037          free(pownorms);
1038          return(ret);
1039       }
1040 
1041 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1042       {  int _w;
1043          fprintf(logfp, "      Power\n");
1044          for(_w = 0; _w < nstats; _w++){
1045             /* Add 1 to wis[w] to create index to original dft_coefs[] */
1046             fprintf(logfp, "         wis[%d] %d %12.3f %2d %9.3f %12.3f\n",
1047                     _w, wis[_w]+1,
1048                     powmaxs[wis[_w]], powmax_dirs[wis[_w]], pownorms[wis[_w]],
1049                     powers[0][powmax_dirs[wis[_w]]]);
1050          }
1051       }
1052 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1053 
1054       /* Conduct primary direction test */
1055       blkdir = primary_dir_test(powers, wis, powmaxs, powmax_dirs,
1056                                pownorms, nstats, lfsparms);
1057 
1058       if(blkdir != INVALID_DIR)
1059          imap[bi] = blkdir;
1060       else{
1061          /* Conduct secondary (fork) direction test */
1062          blkdir = secondary_fork_test(powers, wis, powmaxs, powmax_dirs,
1063                                pownorms, nstats, lfsparms);
1064          if(blkdir != INVALID_DIR)
1065             imap[bi] = blkdir;
1066       }
1067 
1068       /* Otherwise current block direction in IMAP remains INVALID */
1069 
1070    } /* bi */
1071 
1072    /* Deallocate working memory */
1073    free_dir_powers(powers, dftwaves->nwaves);
1074    free(wis);
1075    free(powmaxs);
1076    free(powmax_dirs);
1077    free(pownorms);
1078 
1079    *optr = imap;
1080    return(0);
1081 }
1082 
1083 /*************************************************************************
1084 **************************************************************************
1085 #cat: primary_dir_test - Applies the primary set of criteria for selecting
1086 #cat:                    an IMAP integer direction from a set of DFT results
1087 #cat:                    computed from a block of image data
1088 
1089    Input:
1090       powers      - DFT power computed from each (N) wave frequencies at each
1091                     rotation direction in the current image block
1092       wis         - sorted order of the highest N-1 frequency power statistics
1093       powmaxs     - maximum power for each of the highest N-1 frequencies
1094       powmax_dirs - directions associated with each of the N-1 maximum powers
1095       pownorms    - normalized power for each of the highest N-1 frequencies
1096       nstats      - N-1 wave frequencies (where N is the length of dft_coefs)
1097       lfsparms    - parameters and thresholds for controlling LFS
1098    Return Code:
1099       Zero or Positive - The selected IMAP integer direction
1100       INVALID_DIR - IMAP Integer direction could not be determined
1101 **************************************************************************/
primary_dir_test(double ** powers,const int * wis,const double * powmaxs,const int * powmax_dirs,const double * pownorms,const int nstats,const LFSPARMS * lfsparms)1102 int primary_dir_test(double **powers, const int *wis,
1103             const double *powmaxs, const int *powmax_dirs,
1104             const double *pownorms, const int nstats,
1105             const LFSPARMS *lfsparms)
1106 {
1107    int w;
1108 
1109    print2log("      Primary\n");
1110 
1111    /* Look at max power statistics in decreasing order ... */
1112    for(w = 0; w < nstats; w++){
1113          /* 1. Test magnitude of current max power (Ex. Thresh==100000)   */
1114       if((powmaxs[wis[w]] > lfsparms->powmax_min) &&
1115          /* 2. Test magnitude of normalized max power (Ex. Thresh==3.8)   */
1116          (pownorms[wis[w]] > lfsparms->pownorm_min) &&
1117          /* 3. Test magnitude of power of lowest DFT frequency at current */
1118          /* max power direction and make sure it is not too big.          */
1119          /* (Ex. Thresh==50000000)                                        */
1120          (powers[0][powmax_dirs[wis[w]]] <= lfsparms->powmax_max)){
1121 
1122 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1123             /* Add 1 to wis[w] to create index to original dft_coefs[] */
1124             fprintf(logfp,
1125                      "         Selected Wave = %d\n", wis[w]+1);
1126             fprintf(logfp,
1127                     "         1. Power Magnitude (%12.3f > %12.3f)\n",
1128                    powmaxs[wis[w]], lfsparms->powmax_min);
1129             fprintf(logfp,
1130                     "         2. Norm Power Magnitude (%9.3f > %9.3f)\n",
1131                     pownorms[wis[w]], lfsparms->pownorm_min);
1132             fprintf(logfp,
1133                     "         3. Low Freq Wave Magnitude (%12.3f <= %12.3f)\n",
1134                     powers[0][powmax_dirs[wis[w]]], lfsparms->powmax_max);
1135             fprintf(logfp,
1136                     "         PASSED\n");
1137             fprintf(logfp,
1138                     "         Selected Direction = %d\n",
1139                     powmax_dirs[wis[w]]);
1140 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1141 
1142             /* If ALL 3 criteria met, return current max power direction. */
1143             return(powmax_dirs[wis[w]]);
1144 
1145       }
1146    }
1147 
1148    /* Otherwise test failed. */
1149 
1150 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1151    fprintf(logfp, "         1. Power Magnitude ( > %12.3f)\n",
1152            lfsparms->powmax_min);
1153    fprintf(logfp, "         2. Norm Power Magnitude ( > %9.3f)\n",
1154            lfsparms->pownorm_min);
1155    fprintf(logfp, "         3. Low Freq Wave Magnitude ( <= %12.3f)\n",
1156            lfsparms->powmax_max);
1157    fprintf(logfp, "         FAILED\n");
1158 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1159 
1160    return(INVALID_DIR);
1161 }
1162 
1163 /*************************************************************************
1164 **************************************************************************
1165 #cat: secondary_fork_test - Applies a secondary set of criteria for selecting
1166 #cat:                    an IMAP integer direction from a set of DFT results
1167 #cat:                    computed from a block of image data.  This test
1168 #cat:                    analyzes the strongest power statistics associated
1169 #cat:                    with a given frequency and direction and analyses
1170 #cat:                    small changes in direction to the left and right to
1171 #cat:                    determine if the block contains a "fork".
1172 
1173    Input:
1174       powers      - DFT power computed from each (N) wave frequencies at each
1175                     rotation direction in the current image block
1176       wis         - sorted order of the highest N-1 frequency power statistics
1177       powmaxs     - maximum power for each of the highest N-1 frequencies
1178       powmax_dirs - directions associated with each of the N-1 maximum powers
1179       pownorms    - normalized power for each of the highest N-1 frequencies
1180       nstats      - N-1 wave frequencies (where N is the length of dft_coefs)
1181       lfsparms    - parameters and thresholds for controlling LFS
1182    Return Code:
1183       Zero or Positive - The selected IMAP integer direction
1184       INVALID_DIR - IMAP Integer direction could not be determined
1185 **************************************************************************/
secondary_fork_test(double ** powers,const int * wis,const double * powmaxs,const int * powmax_dirs,const double * pownorms,const int nstats,const LFSPARMS * lfsparms)1186 int secondary_fork_test(double **powers, const int *wis,
1187             const double *powmaxs, const int *powmax_dirs,
1188             const double *pownorms, const int nstats,
1189             const LFSPARMS *lfsparms)
1190 {
1191    int ldir, rdir;
1192    double fork_pownorm_min, fork_pow_thresh;
1193 
1194 #ifdef LOG_REPORT
1195 {  int firstpart = 0; /* Flag to determine if passed 1st part ... */
1196    fprintf(logfp, "      Secondary\n");
1197 #endif
1198 
1199    /* Relax the normalized power threshold under fork conditions. */
1200    fork_pownorm_min = lfsparms->fork_pct_pownorm * lfsparms->pownorm_min;
1201 
1202       /* 1. Test magnitude of largest max power (Ex. Thresh==100000)   */
1203    if((powmaxs[wis[0]] > lfsparms->powmax_min) &&
1204       /* 2. Test magnitude of corresponding normalized power           */
1205       /*    (Ex. Thresh==2.85)                                         */
1206       (pownorms[wis[0]] >= fork_pownorm_min) &&
1207       /* 3. Test magnitude of power of lowest DFT frequency at largest */
1208       /* max power direction and make sure it is not too big.          */
1209       /* (Ex. Thresh==50000000)                                        */
1210       (powers[0][powmax_dirs[wis[0]]] <= lfsparms->powmax_max)){
1211 
1212 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1213       /* First part passed ... */
1214       firstpart = 1;
1215       fprintf(logfp,
1216               "         Selected Wave = %d\n", wis[0]+1);
1217       fprintf(logfp,
1218               "         1. Power Magnitude (%12.3f > %12.3f)\n",
1219              powmaxs[wis[0]], lfsparms->powmax_min);
1220       fprintf(logfp,
1221               "         2. Norm Power Magnitude (%9.3f >= %9.3f)\n",
1222              pownorms[wis[0]], fork_pownorm_min);
1223       fprintf(logfp,
1224               "         3. Low Freq Wave Magnitude (%12.3f <= %12.3f)\n",
1225               powers[0][powmax_dirs[wis[0]]], lfsparms->powmax_max);
1226 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1227 
1228       /* Add FORK_INTERVALs to current direction modulo NDIRS */
1229       rdir = (powmax_dirs[wis[0]] + lfsparms->fork_interval) %
1230                 lfsparms->num_directions;
1231 
1232       /* Subtract FORK_INTERVALs from direction modulo NDIRS  */
1233       /* For example, FORK_INTERVAL==2 & NDIRS==16, then      */
1234       /*            ldir = (dir - (16-2)) % 16                */
1235       /* which keeps result in proper modulo range.           */
1236       ldir = (powmax_dirs[wis[0]] + lfsparms->num_directions -
1237                  lfsparms->fork_interval) % lfsparms->num_directions;
1238 
1239       print2log("         Left = %d, Current = %d, Right = %d\n",
1240               ldir, powmax_dirs[wis[0]], rdir);
1241 
1242       /* Set forked angle threshold to be a % of the max directional */
1243       /* power. (Ex. thresh==0.7*powmax)                             */
1244       fork_pow_thresh = powmaxs[wis[0]] * lfsparms->fork_pct_powmax;
1245 
1246       /* Look up and test the computed power for the left and right    */
1247       /* fork directions.s                                             */
1248       /* The power stats (and thus wis) are on the range [0..nwaves-1) */
1249       /* as the statistics for the first DFT wave are not included.    */
1250       /* The original power vectors exist for ALL DFT waves, therefore */
1251       /* wis indices must be added by 1 before addressing the original */
1252       /* powers vector.                                                */
1253       /* LFS permits one and only one of the fork angles to exceed     */
1254       /* the relative power threshold.                                 */
1255       if(((powers[wis[0]+1][ldir] <= fork_pow_thresh) ||
1256           (powers[wis[0]+1][rdir] <= fork_pow_thresh)) &&
1257          ((powers[wis[0]+1][ldir] > fork_pow_thresh) ||
1258           (powers[wis[0]+1][rdir] > fork_pow_thresh))){
1259 
1260 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1261          fprintf(logfp,
1262                  "         4. Left Power Magnitude (%12.3f > %12.3f)\n",
1263                  powers[wis[0]+1][ldir], fork_pow_thresh);
1264          fprintf(logfp,
1265                  "         5. Right Power Magnitude (%12.3f > %12.3f)\n",
1266                  powers[wis[0]+1][rdir], fork_pow_thresh);
1267          fprintf(logfp, "         PASSED\n");
1268          fprintf(logfp,
1269                  "         Selected Direction = %d\n", powmax_dirs[wis[0]]);
1270 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1271 
1272          /* If ALL the above criteria hold, then return the direction */
1273          /* of the largest max power.                                 */
1274          return(powmax_dirs[wis[0]]);
1275       }
1276    }
1277 
1278    /* Otherwise test failed. */
1279 
1280 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1281    if(!firstpart){
1282       fprintf(logfp,
1283               "         1. Power Magnitude ( > %12.3f)\n",
1284               lfsparms->powmax_min);
1285       fprintf(logfp,
1286               "         2. Norm Power Magnitude ( > %9.3f)\n",
1287               fork_pownorm_min);
1288       fprintf(logfp,
1289               "         3. Low Freq Wave Magnitude ( <= %12.3f)\n",
1290               lfsparms->powmax_max);
1291    }
1292    else{
1293       fprintf(logfp, "         4. Left Power Magnitude (%12.3f > %12.3f)\n",
1294               powers[wis[0]+1][ldir], fork_pow_thresh);
1295       fprintf(logfp, "         5. Right Power Magnitude (%12.3f > %12.3f)\n",
1296               powers[wis[0]+1][rdir], fork_pow_thresh);
1297    }
1298    fprintf(logfp, "         FAILED\n");
1299 } /* Close scope of firstpart */
1300 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1301 
1302    return(INVALID_DIR);
1303 }
1304 
1305 /*************************************************************************
1306 **************************************************************************
1307 #cat: remove_incon_dirs - Takes a vector of integer directions and removes
1308 #cat:              individual directions that are too weak or inconsistent.
1309 #cat:              Directions are tested from the center of the IMAP working
1310 #cat:              outward in concentric squares, and the process resets to
1311 #cat:              the center and continues until no changes take place during
1312 #cat:              a complete pass.
1313 
1314    Input:
1315       imap      - vector of IMAP integer directions
1316       mw        - width (in blocks) of the IMAP
1317       mh        - height (in blocks) of the IMAP
1318       dir2rad   - lookup table for converting integer directions
1319       lfsparms  - parameters and thresholds for controlling LFS
1320    Output:
1321       imap      - vector of pruned input values
1322 **************************************************************************/
remove_incon_dirs(int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1323 void remove_incon_dirs(int *imap, const int mw, const int mh,
1324              const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
1325 {
1326    int cx, cy;
1327    int *iptr;
1328    int nremoved;
1329    int lbox, rbox, tbox, bbox;
1330 
1331 #ifdef LOG_REPORT
1332 {  int numpass = 0;
1333    fprintf(logfp, "REMOVE MAP\n");
1334 #endif
1335 
1336    /* Compute center coords of IMAP */
1337    cx = mw>>1;
1338    cy = mh>>1;
1339 
1340    /* Do pass, while directions have been removed in a pass ... */
1341    do{
1342 
1343 #ifdef LOG_REPORT
1344       /* Count number of complete passes through IMAP */
1345       ++numpass;
1346       fprintf(logfp, "   PASS = %d\n", numpass);
1347 #endif
1348 
1349       /* Reinitialize number of removed directions to 0 */
1350       nremoved = 0;
1351 
1352       /* Start at center */
1353       iptr = imap + (cy * mw) + cx;
1354       /* If valid IMAP direction and test for removal is true ... */
1355       if((*iptr != INVALID_DIR)&&
1356          (remove_dir(imap, cx, cy, mw, mh, dir2rad, lfsparms))){
1357 
1358          /* Set to INVALID */
1359          *iptr = INVALID_DIR;
1360          /* Bump number of removed IMAP directions */
1361          nremoved++;
1362       }
1363 
1364       /* Initialize side indices of concentric boxes */
1365       lbox = cx-1;
1366       tbox = cy-1;
1367       rbox = cx+1;
1368       bbox = cy+1;
1369 
1370       /* Grow concentric boxes, until ALL edges of imap are exceeded */
1371       while((lbox >= 0) || (rbox < mw) || (tbox >= 0) || (bbox < mh)){
1372 
1373          /* test top edge of box */
1374          if(tbox >= 0)
1375             nremoved += test_top_edge(lbox, tbox, rbox, bbox, imap, mw, mh,
1376                              dir2rad, lfsparms);
1377 
1378          /* test right edge of box */
1379          if(rbox < mw)
1380             nremoved += test_right_edge(lbox, tbox, rbox, bbox, imap, mw, mh,
1381                              dir2rad, lfsparms);
1382 
1383          /* test bottom edge of box */
1384          if(bbox < mh)
1385             nremoved += test_bottom_edge(lbox, tbox, rbox, bbox, imap, mw, mh,
1386                              dir2rad, lfsparms);
1387 
1388          /* test left edge of box */
1389          if(lbox >=0)
1390             nremoved += test_left_edge(lbox, tbox, rbox, bbox, imap, mw, mh,
1391                              dir2rad, lfsparms);
1392 
1393          /* Resize current box */
1394          lbox--;
1395          tbox--;
1396          rbox++;
1397          bbox++;
1398       }
1399    }while(nremoved);
1400 
1401 #ifdef LOG_REPORT
1402 } /* Close scope of numpass */
1403 #endif
1404 
1405 }
1406 
1407 /*************************************************************************
1408 **************************************************************************
1409 #cat: test_top_edge - Walks the top edge of a concentric square in the IMAP,
1410 #cat:                 testing directions along the way to see if they should
1411 #cat:                 be removed due to being too weak or inconsistent with
1412 #cat:                 respect to their adjacent neighbors.
1413 
1414    Input:
1415       lbox      - left edge of current concentric square
1416       tbox      - top edge of current concentric square
1417       rbox      - right edge of current concentric square
1418       bbox      - bottom edge of current concentric square
1419       imap      - vector of IMAP integer directions
1420       mw        - width (in blocks) of the IMAP
1421       mh        - height (in blocks) of the IMAP
1422       dir2rad   - lookup table for converting integer directions
1423       lfsparms  - parameters and thresholds for controlling LFS
1424    Return Code:
1425       Positive - direction should be removed from IMAP
1426       Zero     - direction should NOT be remove from IMAP
1427 **************************************************************************/
test_top_edge(const int lbox,const int tbox,const int rbox,const int bbox,int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1428 int test_top_edge(const int lbox, const int tbox, const int rbox,
1429                   const int bbox, int *imap, const int mw, const int mh,
1430                   const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
1431 {
1432    int bx, by, sx, ex;
1433    int *iptr, *sptr, *eptr;
1434    int nremoved;
1435 
1436    /* Initialize number of directions removed on edge to 0 */
1437    nremoved = 0;
1438 
1439    /* Set start pointer to top-leftmost point of box, or set it to */
1440    /* the leftmost point in the IMAP row (0), whichever is larger. */
1441    sx = max(lbox, 0);
1442    sptr = imap + (tbox*mw) + sx;
1443 
1444    /* Set end pointer to either 1 point short of the top-rightmost */
1445    /* point of box, or set it to the rightmost point in the IMAP   */
1446    /* row (lastx=mw-1), whichever is smaller.                      */
1447    ex = min(rbox-1, mw-1);
1448    eptr = imap + (tbox*mw) + ex;
1449 
1450    /* For each point on box's edge ... */
1451    for(iptr = sptr, bx = sx, by = tbox;
1452        iptr <= eptr;
1453        iptr++, bx++){
1454       /* If valid IMAP direction and test for removal is true ... */
1455       if((*iptr != INVALID_DIR)&&
1456          (remove_dir(imap, bx, by, mw, mh, dir2rad, lfsparms))){
1457          /* Set to INVALID */
1458          *iptr = INVALID_DIR;
1459          /* Bump number of removed IMAP directions */
1460          nremoved++;
1461       }
1462    }
1463 
1464    /* Return the number of directions removed on edge */
1465    return(nremoved);
1466 }
1467 
1468 /*************************************************************************
1469 **************************************************************************
1470 #cat: test_right_edge - Walks the right edge of a concentric square in the
1471 #cat:                 IMAP, testing directions along the way to see if they
1472 #cat:                 should be removed due to being too weak or inconsistent
1473 #cat:                 with respect to their adjacent neighbors.
1474 
1475    Input:
1476       lbox      - left edge of current concentric square
1477       tbox      - top edge of current concentric square
1478       rbox      - right edge of current concentric square
1479       bbox      - bottom edge of current concentric square
1480       imap      - vector of IMAP integer directions
1481       mw        - width (in blocks) of the IMAP
1482       mh        - height (in blocks) of the IMAP
1483       dir2rad   - lookup table for converting integer directions
1484       lfsparms  - parameters and thresholds for controlling LFS
1485    Return Code:
1486       Positive - direction should be removed from IMAP
1487       Zero     - direction should NOT be remove from IMAP
1488 **************************************************************************/
test_right_edge(const int lbox,const int tbox,const int rbox,const int bbox,int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1489 int test_right_edge(const int lbox, const int tbox, const int rbox,
1490                     const int bbox, int *imap, const int mw, const int mh,
1491                     const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
1492 {
1493    int bx, by, sy, ey;
1494    int *iptr, *sptr, *eptr;
1495    int nremoved;
1496 
1497    /* Initialize number of directions removed on edge to 0 */
1498    nremoved = 0;
1499 
1500    /* Set start pointer to top-rightmost point of box, or set it to */
1501    /* the topmost point in IMAP column (0), whichever is larger.    */
1502    sy = max(tbox, 0);
1503    sptr = imap + (sy*mw) + rbox;
1504 
1505    /* Set end pointer to either 1 point short of the bottom-    */
1506    /* rightmost point of box, or set it to the bottommost point */
1507    /* in the IMAP column (lasty=mh-1), whichever is smaller.    */
1508    ey = min(bbox-1,mh-1);
1509    eptr = imap + (ey*mw) + rbox;
1510 
1511    /* For each point on box's edge ... */
1512    for(iptr = sptr, bx = rbox, by = sy;
1513        iptr <= eptr;
1514        iptr+=mw, by++){
1515       /* If valid IMAP direction and test for removal is true ... */
1516       if((*iptr != INVALID_DIR)&&
1517          (remove_dir(imap, bx, by, mw, mh, dir2rad, lfsparms))){
1518          /* Set to INVALID */
1519          *iptr = INVALID_DIR;
1520          /* Bump number of removed IMAP directions */
1521          nremoved++;
1522       }
1523    }
1524 
1525    /* Return the number of directions removed on edge */
1526    return(nremoved);
1527 }
1528 
1529 /*************************************************************************
1530 **************************************************************************
1531 #cat: test_bottom_edge - Walks the bottom edge of a concentric square in the
1532 #cat:               IMAP, testing directions along the way to see if they
1533 #cat:               should be removed due to being too weak or inconsistent
1534 #cat:               with respect to their adjacent neighbors.
1535    Input:
1536       lbox      - left edge of current concentric square
1537       tbox      - top edge of current concentric square
1538       rbox      - right edge of current concentric square
1539       bbox      - bottom edge of current concentric square
1540       imap      - vector of IMAP integer directions
1541       mw        - width (in blocks) of the IMAP
1542       mh        - height (in blocks) of the IMAP
1543       dir2rad   - lookup table for converting integer directions
1544       lfsparms  - parameters and thresholds for controlling LFS
1545    Return Code:
1546       Positive - direction should be removed from IMAP
1547       Zero     - direction should NOT be remove from IMAP
1548 **************************************************************************/
test_bottom_edge(const int lbox,const int tbox,const int rbox,const int bbox,int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1549 int test_bottom_edge(const int lbox, const int tbox, const int rbox,
1550                      const int bbox, int *imap, const int mw, const int mh,
1551                      const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
1552 {
1553    int bx, by, sx, ex;
1554    int *iptr, *sptr, *eptr;
1555    int nremoved;
1556 
1557    /* Initialize number of directions removed on edge to 0 */
1558    nremoved = 0;
1559 
1560    /* Set start pointer to bottom-rightmost point of box, or set it to the */
1561    /* rightmost point in the IMAP ROW (lastx=mw-1), whichever is smaller.  */
1562    sx = min(rbox, mw-1);
1563    sptr = imap + (bbox*mw) + sx;
1564 
1565    /* Set end pointer to either 1 point short of the bottom-    */
1566    /* lefttmost point of box, or set it to the leftmost point   */
1567    /* in the IMAP row (x=0), whichever is larger.               */
1568    ex = max(lbox-1, 0);
1569    eptr = imap + (bbox*mw) + ex;
1570 
1571    /* For each point on box's edge ... */
1572    for(iptr = sptr, bx = sx, by = bbox;
1573        iptr >= eptr;
1574        iptr--, bx--){
1575       /* If valid IMAP direction and test for removal is true ... */
1576       if((*iptr != INVALID_DIR)&&
1577          (remove_dir(imap, bx, by, mw, mh, dir2rad, lfsparms))){
1578          /* Set to INVALID */
1579          *iptr = INVALID_DIR;
1580          /* Bump number of removed IMAP directions */
1581          nremoved++;
1582       }
1583    }
1584 
1585    /* Return the number of directions removed on edge */
1586    return(nremoved);
1587 }
1588 
1589 /*************************************************************************
1590 **************************************************************************
1591 #cat: test_left_edge - Walks the left edge of a concentric square in the IMAP,
1592 #cat:                 testing directions along the way to see if they should
1593 #cat:                 be removed due to being too weak or inconsistent with
1594 #cat:                 respect to their adjacent neighbors.
1595 
1596    Input:
1597       lbox      - left edge of current concentric square
1598       tbox      - top edge of current concentric square
1599       rbox      - right edge of current concentric square
1600       bbox      - bottom edge of current concentric square
1601       imap      - vector of IMAP integer directions
1602       mw        - width (in blocks) of the IMAP
1603       mh        - height (in blocks) of the IMAP
1604       dir2rad   - lookup table for converting integer directions
1605       lfsparms  - parameters and thresholds for controlling LFS
1606    Return Code:
1607       Positive - direction should be removed from IMAP
1608       Zero     - direction should NOT be remove from IMAP
1609 **************************************************************************/
test_left_edge(const int lbox,const int tbox,const int rbox,const int bbox,int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1610 int test_left_edge(const int lbox, const int tbox, const int rbox,
1611                    const int bbox, int *imap, const int mw, const int mh,
1612                    const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
1613 {
1614    int bx, by, sy, ey;
1615    int *iptr, *sptr, *eptr;
1616    int nremoved;
1617 
1618    /* Initialize number of directions removed on edge to 0 */
1619    nremoved = 0;
1620 
1621    /* Set start pointer to bottom-leftmost point of box, or set it to */
1622    /* the bottommost point in IMAP column (lasty=mh-1), whichever     */
1623    /* is smaller.                                                     */
1624    sy = min(bbox, mh-1);
1625    sptr = imap + (sy*mw) + lbox;
1626 
1627    /* Set end pointer to either 1 point short of the top-leftmost */
1628    /* point of box, or set it to the topmost point in the IMAP    */
1629    /* column (y=0), whichever is larger.                          */
1630    ey = max(tbox-1, 0);
1631    eptr = imap + (ey*mw) + lbox;
1632 
1633    /* For each point on box's edge ... */
1634    for(iptr = sptr, bx = lbox, by = sy;
1635        iptr >= eptr;
1636        iptr-=mw, by--){
1637       /* If valid IMAP direction and test for removal is true ... */
1638       if((*iptr != INVALID_DIR)&&
1639          (remove_dir(imap, bx, by, mw, mh, dir2rad, lfsparms))){
1640          /* Set to INVALID */
1641          *iptr = INVALID_DIR;
1642          /* Bump number of removed IMAP directions */
1643          nremoved++;
1644       }
1645    }
1646 
1647    /* Return the number of directions removed on edge */
1648    return(nremoved);
1649 }
1650 
1651 /*************************************************************************
1652 **************************************************************************
1653 #cat: remove_dir - Determines if an IMAP direction should be removed based
1654 #cat:              on analyzing its adjacent neighbors
1655 
1656    Input:
1657       imap      - vector of IMAP integer directions
1658       mx        - IMAP X-coord of the current direction being tested
1659       my        - IMPA Y-coord of the current direction being tested
1660       mw        - width (in blocks) of the IMAP
1661       mh        - height (in blocks) of the IMAP
1662       dir2rad   - lookup table for converting integer directions
1663       lfsparms  - parameters and thresholds for controlling LFS
1664    Return Code:
1665       Positive - direction should be removed from IMAP
1666       Zero     - direction should NOT be remove from IMAP
1667 **************************************************************************/
remove_dir(int * imap,const int mx,const int my,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)1668 int remove_dir(int *imap, const int mx, const int my,
1669                const int mw, const int mh, const DIR2RAD *dir2rad,
1670                const LFSPARMS *lfsparms)
1671 {
1672    int avrdir, nvalid, dist;
1673    double dir_strength;
1674 
1675    /* Compute average direction from neighbors, returning the */
1676    /* number of valid neighbors used in the computation, and  */
1677    /* the "strength" of the average direction.                */
1678    average_8nbr_dir(&avrdir, &dir_strength, &nvalid, imap, mx, my, mw, mh,
1679                     dir2rad);
1680 
1681    /* Conduct valid neighbor test (Ex. thresh==3) */
1682    if(nvalid < lfsparms->rmv_valid_nbr_min){
1683 
1684 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1685       fprintf(logfp, "      BLOCK %2d (%2d, %2d)\n",
1686               mx+(my*mw), mx, my);
1687       fprintf(logfp, "         Average NBR :   %2d %6.3f %d\n",
1688               avrdir, dir_strength, nvalid);
1689       fprintf(logfp, "         1. Valid NBR (%d < %d)\n",
1690               nvalid, lfsparms->rmv_valid_nbr_min);
1691 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1692 
1693       return(1);
1694    }
1695 
1696    /* If stregnth of average neighbor direction is large enough to */
1697    /* put credence in ... (Ex. thresh==0.2)                        */
1698    if(dir_strength >= lfsparms->dir_strength_min){
1699 
1700       /* Conduct direction distance test (Ex. thresh==3) */
1701       /* Compute minimum absolute distance between current and       */
1702       /* average directions accounting for wrapping from 0 to NDIRS. */
1703       dist = abs(avrdir - *(imap+(my*mw)+mx));
1704       dist = min(dist, dir2rad->ndirs-dist);
1705       if(dist > lfsparms->dir_distance_max){
1706 
1707 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
1708          fprintf(logfp, "      BLOCK %2d (%2d, %2d)\n",
1709                  mx+(my*mw), mx, my);
1710          fprintf(logfp, "         Average NBR :   %2d %6.3f %d\n",
1711                  avrdir, dir_strength, nvalid);
1712          fprintf(logfp, "         1. Valid NBR (%d < %d)\n",
1713                  nvalid, lfsparms->rmv_valid_nbr_min);
1714          fprintf(logfp, "         2. Direction Strength (%6.3f >= %6.3f)\n",
1715                  dir_strength, lfsparms->dir_strength_min);
1716          fprintf(logfp, "         Current Dir =  %d, Average Dir = %d\n",
1717                  *(imap+(my*mw)+mx), avrdir);
1718          fprintf(logfp, "         3. Direction Distance (%d > %d)\n",
1719                  dist, lfsparms->dir_distance_max);
1720 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
1721 
1722          return(2);
1723       }
1724    }
1725 
1726    /* Otherwise, the strength of the average direciton is not strong enough */
1727    /* to put credence in, so leave the current block's directon alone.      */
1728 
1729    return(0);
1730 }
1731 
1732 /*************************************************************************
1733 **************************************************************************
1734 #cat: average_8nbr_dir - Given an IMAP direction, computes an average
1735 #cat:                    direction from its adjacent 8 neighbors returning
1736 #cat:                    the average direction, its strength, and the
1737 #cat:                    number of valid direction in the neighborhood.
1738 
1739    Input:
1740       imap      - vector of IMAP integer directions
1741       mx        - IMAP X-coord of the current direction
1742       my        - IMPA Y-coord of the current direction
1743       mw        - width (in blocks) of the IMAP
1744       mh        - height (in blocks) of the IMAP
1745       dir2rad   - lookup table for converting integer directions
1746    Output:
1747       avrdir    - the average direction computed from neighbors
1748       dir_strenght - the strength of the average direction
1749       nvalid    - the number of valid directions used to compute the
1750                   average
1751 **************************************************************************/
average_8nbr_dir(int * avrdir,double * dir_strength,int * nvalid,int * imap,const int mx,const int my,const int mw,const int mh,const DIR2RAD * dir2rad)1752 void average_8nbr_dir(int *avrdir, double *dir_strength, int *nvalid,
1753                       int *imap, const int mx, const int my,
1754                       const int mw, const int mh,
1755                       const DIR2RAD *dir2rad)
1756 {
1757    int *iptr;
1758    int e,w,n,s;
1759    double cospart, sinpart;
1760    double pi2, pi_factor, theta;
1761    double avr;
1762 
1763    /* Compute neighbor coordinates to current IMAP direction */
1764    e = mx+1;  /* East */
1765    w = mx-1;  /* West */
1766    n = my-1;  /* North */
1767    s = my+1;  /* South */
1768 
1769    /* Intialize accumulators */
1770    *nvalid = 0;
1771    cospart = 0.0;
1772    sinpart = 0.0;
1773 
1774    /* 1. Test NW */
1775    /* If NW point within IMAP boudaries ... */
1776    if((w >= 0) && (n >= 0)){
1777       iptr = imap + (n*mw) + w;
1778       /* If valid direction ... */
1779       if(*iptr != INVALID_DIR){
1780          /* Accumulate cosine and sine components of the direction */
1781          cospart += dir2rad->cos[*iptr];
1782          sinpart += dir2rad->sin[*iptr];
1783          /* Bump number of accumulated directions */
1784          (*nvalid)++;
1785       }
1786    }
1787 
1788    /* 2. Test N */
1789    /* If N point within IMAP boudaries ... */
1790    if(n >= 0){
1791       iptr = imap + (n*mw) + mx;
1792       /* If valid direction ... */
1793       if(*iptr != INVALID_DIR){
1794          /* Accumulate cosine and sine components of the direction */
1795          cospart += dir2rad->cos[*iptr];
1796          sinpart += dir2rad->sin[*iptr];
1797          /* Bump number of accumulated directions */
1798          (*nvalid)++;
1799       }
1800    }
1801 
1802    /* 3. Test NE */
1803    /* If NE point within IMAP boudaries ... */
1804    if((e < mw) && (n >= 0)){
1805       iptr = imap + (n*mw) + e;
1806       /* If valid direction ... */
1807       if(*iptr != INVALID_DIR){
1808          /* Accumulate cosine and sine components of the direction */
1809          cospart += dir2rad->cos[*iptr];
1810          sinpart += dir2rad->sin[*iptr];
1811          /* Bump number of accumulated directions */
1812          (*nvalid)++;
1813       }
1814    }
1815 
1816    /* 4. Test E */
1817    /* If E point within IMAP boudaries ... */
1818    if(e < mw){
1819       iptr = imap + (my*mw) + e;
1820       /* If valid direction ... */
1821       if(*iptr != INVALID_DIR){
1822          /* Accumulate cosine and sine components of the direction */
1823          cospart += dir2rad->cos[*iptr];
1824          sinpart += dir2rad->sin[*iptr];
1825          /* Bump number of accumulated directions */
1826          (*nvalid)++;
1827       }
1828    }
1829 
1830    /* 5. Test SE */
1831    /* If SE point within IMAP boudaries ... */
1832    if((e < mw) && (s < mh)){
1833       iptr = imap + (s*mw) + e;
1834       /* If valid direction ... */
1835       if(*iptr != INVALID_DIR){
1836          /* Accumulate cosine and sine components of the direction */
1837          cospart += dir2rad->cos[*iptr];
1838          sinpart += dir2rad->sin[*iptr];
1839          /* Bump number of accumulated directions */
1840          (*nvalid)++;
1841       }
1842    }
1843 
1844    /* 6. Test S */
1845    /* If S point within IMAP boudaries ... */
1846    if(s < mh){
1847       iptr = imap + (s*mw) + mx;
1848       /* If valid direction ... */
1849       if(*iptr != INVALID_DIR){
1850          /* Accumulate cosine and sine components of the direction */
1851          cospart += dir2rad->cos[*iptr];
1852          sinpart += dir2rad->sin[*iptr];
1853          /* Bump number of accumulated directions */
1854          (*nvalid)++;
1855       }
1856    }
1857 
1858    /* 7. Test SW */
1859    /* If SW point within IMAP boudaries ... */
1860    if((w >= 0) && (s < mh)){
1861       iptr = imap + (s*mw) + w;
1862       /* If valid direction ... */
1863       if(*iptr != INVALID_DIR){
1864          /* Accumulate cosine and sine components of the direction */
1865          cospart += dir2rad->cos[*iptr];
1866          sinpart += dir2rad->sin[*iptr];
1867          /* Bump number of accumulated directions */
1868          (*nvalid)++;
1869       }
1870    }
1871 
1872    /* 8. Test W */
1873    /* If W point within IMAP boudaries ... */
1874    if(w >= 0){
1875       iptr = imap + (my*mw) + w;
1876       /* If valid direction ... */
1877       if(*iptr != INVALID_DIR){
1878          /* Accumulate cosine and sine components of the direction */
1879          cospart += dir2rad->cos[*iptr];
1880          sinpart += dir2rad->sin[*iptr];
1881          /* Bump number of accumulated directions */
1882          (*nvalid)++;
1883       }
1884    }
1885 
1886    /* If there were no neighbors found with valid direction ... */
1887    if(*nvalid == 0){
1888       /* Return INVALID direction. */
1889       *dir_strength = 0;
1890       *avrdir = INVALID_DIR;
1891       return;
1892    }
1893 
1894    /* Compute averages of accumulated cosine and sine direction components */
1895    cospart /= (double)(*nvalid);
1896    sinpart /= (double)(*nvalid);
1897 
1898    /* Compute directional strength as hypotenuse (without sqrt) of average */
1899    /* cosine and sine direction components.  Believe this value will be on */
1900    /* the range of [0 .. 1].                                               */
1901    *dir_strength = (cospart * cospart) + (sinpart * sinpart);
1902    /* Need to truncate precision so that answers are consistent   */
1903    /* on different computer architectures when comparing doubles. */
1904    *dir_strength = trunc_dbl_precision(*dir_strength, TRUNC_SCALE);
1905 
1906    /* If the direction strength is not sufficiently high ... */
1907    if(*dir_strength < DIR_STRENGTH_MIN){
1908       /* Return INVALID direction. */
1909       *dir_strength = 0;
1910       *avrdir = INVALID_DIR;
1911       return;
1912    }
1913 
1914    /* Compute angle (in radians) from Arctan of avarage         */
1915    /* cosine and sine direction components.  I think this order */
1916    /* is necessary because 0 direction is vertical and positive */
1917    /* direction is clockwise.                                   */
1918    theta = atan2(sinpart, cospart);
1919 
1920    /* Atan2 returns theta on range [-PI..PI].  Adjust theta so that */
1921    /* it is on the range [0..2PI].                                  */
1922    pi2 = 2*M_PI;
1923    theta += pi2;
1924    theta = fmod(theta, pi2);
1925 
1926    /* Pi_factor sets the period of the trig functions to NDIRS units in x. */
1927    /* For example, if NDIRS==16, then pi_factor = 2(PI/16) = .3926...      */
1928    /* Dividing theta (in radians) by this factor ((1/pi_factor)==2.546...) */
1929    /* will produce directions on the range [0..NDIRS].                     */
1930    pi_factor = pi2/(double)dir2rad->ndirs; /* 2(M_PI/ndirs) */
1931 
1932    /* Round off the direction and return it as an average direction */
1933    /* for the neighborhood.                                         */
1934    avr = theta / pi_factor;
1935    /* Need to truncate precision so that answers are consistent */
1936    /* on different computer architectures when rounding doubles. */
1937    avr = trunc_dbl_precision(avr, TRUNC_SCALE);
1938    *avrdir = sround(avr);
1939 
1940    /* Really do need to map values > NDIRS back onto [0..NDIRS) range. */
1941    *avrdir %= dir2rad->ndirs;
1942 }
1943 
1944 /*************************************************************************
1945 **************************************************************************
1946 #cat: num_valid_8nbrs - Given a block in an IMAP, counts the number of
1947 #cat:                   immediate neighbors that have a valid IMAP direction.
1948 
1949    Input:
1950       imap - 2-D vector of directional ridge flows
1951       mx   - horizontal coord of current block in IMAP
1952       my   - vertical coord of current block in IMAP
1953       mw   - width (in blocks) of the IMAP
1954       mh   - height (in blocks) of the IMAP
1955    Return Code:
1956       Non-negative - the number of valid IMAP neighbors
1957 **************************************************************************/
num_valid_8nbrs(int * imap,const int mx,const int my,const int mw,const int mh)1958 int num_valid_8nbrs(int *imap, const int mx, const int my,
1959                     const int mw, const int mh)
1960 {
1961    int e_ind, w_ind, n_ind, s_ind;
1962    int nvalid;
1963 
1964    /* Initialize VALID IMAP counter to zero. */
1965    nvalid = 0;
1966 
1967    /* Compute neighbor coordinates to current IMAP direction */
1968    e_ind = mx+1;  /* East index */
1969    w_ind = mx-1;  /* West index */
1970    n_ind = my-1;  /* North index */
1971    s_ind = my+1;  /* South index */
1972 
1973    /* 1. Test NW IMAP value.  */
1974    /* If neighbor indices are within IMAP boundaries and it is VALID ... */
1975    if((w_ind >= 0) && (n_ind >= 0) && (*(imap + (n_ind*mw) + w_ind) >= 0))
1976       /* Bump VALID counter. */
1977       nvalid++;
1978 
1979    /* 2. Test N IMAP value.  */
1980    if((n_ind >= 0) && (*(imap + (n_ind*mw) + mx) >= 0))
1981       nvalid++;
1982 
1983    /* 3. Test NE IMAP value. */
1984    if((n_ind >= 0) && (e_ind < mw) && (*(imap + (n_ind*mw) + e_ind) >= 0))
1985       nvalid++;
1986 
1987    /* 4. Test E IMAP value. */
1988    if((e_ind < mw) && (*(imap + (my*mw) + e_ind) >= 0))
1989       nvalid++;
1990 
1991    /* 5. Test SE IMAP value. */
1992    if((e_ind < mw) && (s_ind < mh) && (*(imap + (s_ind*mw) + e_ind) >= 0))
1993       nvalid++;
1994 
1995    /* 6. Test S IMAP value. */
1996    if((s_ind < mh) && (*(imap + (s_ind*mw) + mx) >= 0))
1997       nvalid++;
1998 
1999    /* 7. Test SW IMAP value. */
2000    if((w_ind >= 0) && (s_ind < mh) && (*(imap + (s_ind*mw) + w_ind) >= 0))
2001       nvalid++;
2002 
2003    /* 8. Test W IMAP value. */
2004    if((w_ind >= 0) && (*(imap + (my*mw) + w_ind) >= 0))
2005       nvalid++;
2006 
2007    /* Return number of neighbors with VALID IMAP values. */
2008    return(nvalid);
2009 }
2010 
2011 /*************************************************************************
2012 **************************************************************************
2013 #cat: smooth_imap - Takes a vector of integer directions and smooths them
2014 #cat:               by analyzing the direction of adjacent neighbors.
2015 
2016    Input:
2017       imap      - vector of IMAP integer directions
2018       mw        - width (in blocks) of the IMAP
2019       mh        - height (in blocks) of the IMAP
2020       dir2rad   - lookup table for converting integer directions
2021       lfsparms  - parameters and thresholds for controlling LFS
2022    Output:
2023       imap      - vector of smoothed input values
2024 **************************************************************************/
smooth_imap(int * imap,const int mw,const int mh,const DIR2RAD * dir2rad,const LFSPARMS * lfsparms)2025 void smooth_imap(int *imap, const int mw, const int mh,
2026                  const DIR2RAD *dir2rad, const LFSPARMS *lfsparms)
2027 {
2028    int mx, my;
2029    int *iptr;
2030    int avrdir, nvalid;
2031    double dir_strength;
2032 
2033    print2log("SMOOTH MAP\n");
2034 
2035    iptr = imap;
2036    for(my = 0; my < mh; my++){
2037       for(mx = 0; mx < mw; mx++){
2038          /* Compute average direction from neighbors, returning the */
2039          /* number of valid neighbors used in the computation, and  */
2040          /* the "strength" of the average direction.                */
2041          average_8nbr_dir(&avrdir, &dir_strength, &nvalid,
2042                           imap, mx, my, mw, mh, dir2rad);
2043 
2044          /* If average direction strength is strong enough */
2045          /*    (Ex. thresh==0.2)...                        */
2046          if(dir_strength >= lfsparms->dir_strength_min){
2047             /* If IMAP direction is valid ... */
2048             if(*iptr != INVALID_DIR){
2049                /* Conduct valid neighbor test (Ex. thresh==3)... */
2050                if(nvalid >= lfsparms->rmv_valid_nbr_min){
2051 
2052 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
2053                   fprintf(logfp, "   BLOCK %2d (%2d, %2d)\n",
2054                           mx+(my*mw), mx, my);
2055                   fprintf(logfp, "      Average NBR :   %2d %6.3f %d\n",
2056                           avrdir, dir_strength, nvalid);
2057                   fprintf(logfp, "      1. Valid NBR (%d >= %d)\n",
2058                           nvalid, lfsparms->rmv_valid_nbr_min);
2059                   fprintf(logfp, "      Valid Direction = %d\n", *iptr);
2060                   fprintf(logfp, "      Smoothed Direction = %d\n", avrdir);
2061 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
2062 
2063                   /* Reassign valid IMAP direction with average direction. */
2064                   *iptr = avrdir;
2065                }
2066             }
2067             /* Otherwise IMAP direction is invalid ... */
2068             else{
2069                /* Even if IMAP value is invalid, if number of valid */
2070                /* neighbors is big enough (Ex. thresh==7)...        */
2071                if(nvalid >= lfsparms->smth_valid_nbr_min){
2072 
2073 #ifdef LOG_REPORT /*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
2074                   fprintf(logfp, "   BLOCK %2d (%2d, %2d)\n",
2075                           mx+(my*mw), mx, my);
2076                   fprintf(logfp, "      Average NBR :   %2d %6.3f %d\n",
2077                           avrdir, dir_strength, nvalid);
2078                   fprintf(logfp, "      2. Invalid NBR (%d >= %d)\n",
2079                           nvalid, lfsparms->smth_valid_nbr_min);
2080                   fprintf(logfp, "      Invalid Direction = %d\n", *iptr);
2081                   fprintf(logfp, "      Smoothed Direction = %d\n", avrdir);
2082 #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
2083 
2084                   /* Assign invalid IMAP direction with average direction. */
2085                   *iptr = avrdir;
2086                }
2087             }
2088          }
2089 
2090          /* Bump to next IMAP direction. */
2091          iptr++;
2092       }
2093    }
2094 }
2095 
2096 /*************************************************************************
2097 **************************************************************************
2098 #cat: vorticity - Measures the amount of cummulative curvature incurred
2099 #cat:             among the IMAP neighbors of the given block.
2100 
2101    Input:
2102       imap  - 2D vector of ridge flow directions
2103       mx    - horizontal coord of current IMAP block
2104       my    - vertical coord of current IMAP block
2105       mw    - width (in blocks) of the IMAP
2106       mh    - height (in blocks) of the IMAP
2107       ndirs - number of possible directions in the IMAP
2108    Return Code:
2109       Non-negative - the measured vorticity among the neighbors
2110 **************************************************************************/
vorticity(int * imap,const int mx,const int my,const int mw,const int mh,const int ndirs)2111 int vorticity(int *imap, const int mx, const int my,
2112               const int mw, const int mh, const int ndirs)
2113 {
2114    int e_ind, w_ind, n_ind, s_ind;
2115    int nw_val, n_val, ne_val, e_val, se_val, s_val, sw_val, w_val;
2116    int vmeasure;
2117 
2118    /* Compute neighbor coordinates to current IMAP direction */
2119    e_ind = mx+1;  /* East index */
2120    w_ind = mx-1;  /* West index */
2121    n_ind = my-1;  /* North index */
2122    s_ind = my+1;  /* South index */
2123 
2124    /* 1. Get NW IMAP value.  */
2125    /* If neighbor indices are within IMAP boundaries ... */
2126    if((w_ind >= 0) && (n_ind >= 0))
2127       /* Set neighbor value to IMAP value. */
2128       nw_val = *(imap + (n_ind*mw) + w_ind);
2129    else
2130       /* Otherwise, set the neighbor value to INVALID. */
2131       nw_val = INVALID_DIR;
2132 
2133    /* 2. Get N IMAP value.  */
2134    if(n_ind >= 0)
2135       n_val = *(imap + (n_ind*mw) + mx);
2136    else
2137       n_val = INVALID_DIR;
2138 
2139    /* 3. Get NE IMAP value. */
2140    if((n_ind >= 0) && (e_ind < mw))
2141       ne_val = *(imap + (n_ind*mw) + e_ind);
2142    else
2143       ne_val = INVALID_DIR;
2144 
2145    /* 4. Get E IMAP value. */
2146    if(e_ind < mw)
2147       e_val = *(imap + (my*mw) + e_ind);
2148    else
2149       e_val = INVALID_DIR;
2150 
2151    /* 5. Get SE IMAP value. */
2152    if((e_ind < mw) && (s_ind < mh))
2153       se_val = *(imap + (s_ind*mw) + e_ind);
2154    else
2155       se_val = INVALID_DIR;
2156 
2157    /* 6. Get S IMAP value. */
2158    if(s_ind < mh)
2159       s_val = *(imap + (s_ind*mw) + mx);
2160    else
2161       s_val = INVALID_DIR;
2162 
2163    /* 7. Get SW IMAP value. */
2164    if((w_ind >= 0) && (s_ind < mh))
2165       sw_val = *(imap + (s_ind*mw) + w_ind);
2166    else
2167       sw_val = INVALID_DIR;
2168 
2169    /* 8. Get W IMAP value. */
2170    if(w_ind >= 0)
2171       w_val = *(imap + (my*mw) + w_ind);
2172    else
2173       w_val = INVALID_DIR;
2174 
2175    /* Now that we have all IMAP neighbors, accumulate vorticity between */
2176    /* the neighboring directions.                                       */
2177 
2178    /* Initialize vorticity accumulator to zero. */
2179    vmeasure = 0;
2180 
2181    /* 1. NW & N */
2182    accum_nbr_vorticity(&vmeasure, nw_val, n_val, ndirs);
2183 
2184    /* 2. N & NE */
2185    accum_nbr_vorticity(&vmeasure, n_val, ne_val, ndirs);
2186 
2187    /* 3. NE & E */
2188    accum_nbr_vorticity(&vmeasure, ne_val, e_val, ndirs);
2189 
2190    /* 4. E & SE */
2191    accum_nbr_vorticity(&vmeasure, e_val, se_val, ndirs);
2192 
2193    /* 5. SE & S */
2194    accum_nbr_vorticity(&vmeasure, se_val, s_val, ndirs);
2195 
2196    /* 6. S & SW */
2197    accum_nbr_vorticity(&vmeasure, s_val, sw_val, ndirs);
2198 
2199    /* 7. SW & W */
2200    accum_nbr_vorticity(&vmeasure, sw_val, w_val, ndirs);
2201 
2202    /* 8. W & NW */
2203    accum_nbr_vorticity(&vmeasure, w_val, nw_val, ndirs);
2204 
2205    /* Return the accumulated vorticity measure. */
2206    return(vmeasure);
2207 }
2208 
2209 /*************************************************************************
2210 **************************************************************************
2211 #cat: accum_nbor_vorticity - Accumlates the amount of curvature measures
2212 #cat:                        between neighboring IMAP blocks.
2213 
2214    Input:
2215       dir1  - first neighbor's integer IMAP direction
2216       dir2  - second neighbor's integer IMAP direction
2217       ndirs - number of possible IMAP directions
2218    Output:
2219       vmeasure - accumulated vorticity among neighbors measured so far
2220 **************************************************************************/
accum_nbr_vorticity(int * vmeasure,const int dir1,const int dir2,const int ndirs)2221 void accum_nbr_vorticity(int *vmeasure, const int dir1, const int dir2,
2222                          const int ndirs)
2223 {
2224    int dist;
2225 
2226    /* Measure difference in direction between a pair of neighboring */
2227    /* directions.                                                   */
2228    /* If both neighbors are not equal and both are VALID ... */
2229    if((dir1 != dir2) && (dir1 >= 0)&&(dir2 >= 0)){
2230       /* Measure the clockwise distance from the first to the second */
2231       /* directions.                                                 */
2232       dist = dir2 - dir1;
2233       /* If dist is negative, then clockwise distance must wrap around */
2234       /* the high end of the direction range. For example:             */
2235       /*              dir1 = 8                                         */
2236       /*              dir2 = 3                                         */
2237       /*       and   ndirs = 16                                        */
2238       /*             3 - 8 = -5                                        */
2239       /*        so  16 - 5 = 11  (the clockwise distance from 8 to 3)  */
2240        if(dist < 0)
2241          dist += ndirs;
2242       /* If the change in clockwise direction is larger than 90 degrees as */
2243       /* in total the total number of directions covers 180 degrees.       */
2244       if(dist > (ndirs>>1))
2245          /* Decrement the vorticity measure. */
2246          (*vmeasure)--;
2247       else
2248          /* Otherwise, bump the vorticity measure. */
2249          (*vmeasure)++;
2250    }
2251    /* Otherwise both directions are either equal or  */
2252    /* one or both directions are INVALID, so ignore. */
2253 }
2254 
2255 /*************************************************************************
2256 **************************************************************************
2257 #cat: curvature - Measures the largest change in direction between the
2258 #cat:             current IMAP direction and its immediate neighbors.
2259 
2260    Input:
2261       imap  - 2D vector of ridge flow directions
2262       mx    - horizontal coord of current IMAP block
2263       my    - vertical coord of current IMAP block
2264       mw    - width (in blocks) of the IMAP
2265       mh    - height (in blocks) of the IMAP
2266       ndirs - number of possible directions in the IMAP
2267    Return Code:
2268       Non-negative - maximum change in direction found (curvature)
2269       Negative     - No valid neighbor found to measure change in direction
2270 **************************************************************************/
curvature(int * imap,const int mx,const int my,const int mw,const int mh,const int ndirs)2271 int curvature(int *imap, const int mx, const int my,
2272               const int mw, const int mh, const int ndirs)
2273 {
2274    int *iptr;
2275    int e_ind, w_ind, n_ind, s_ind;
2276    int nw_val, n_val, ne_val, e_val, se_val, s_val, sw_val, w_val;
2277    int cmeasure, dist;
2278 
2279    /* Compute neighbor coordinates to current IMAP direction */
2280    e_ind = mx+1;  /* East index */
2281    w_ind = mx-1;  /* West index */
2282    n_ind = my-1;  /* North index */
2283    s_ind = my+1;  /* South index */
2284 
2285    /* 1. Get NW IMAP value.  */
2286    /* If neighbor indices are within IMAP boundaries ... */
2287    if((w_ind >= 0) && (n_ind >= 0))
2288       /* Set neighbor value to IMAP value. */
2289       nw_val = *(imap + (n_ind*mw) + w_ind);
2290    else
2291       /* Otherwise, set the neighbor value to INVALID. */
2292       nw_val = INVALID_DIR;
2293 
2294    /* 2. Get N IMAP value.  */
2295    if(n_ind >= 0)
2296       n_val = *(imap + (n_ind*mw) + mx);
2297    else
2298       n_val = INVALID_DIR;
2299 
2300    /* 3. Get NE IMAP value. */
2301    if((n_ind >= 0) && (e_ind < mw))
2302       ne_val = *(imap + (n_ind*mw) + e_ind);
2303    else
2304       ne_val = INVALID_DIR;
2305 
2306    /* 4. Get E IMAP value. */
2307    if(e_ind < mw)
2308       e_val = *(imap + (my*mw) + e_ind);
2309    else
2310       e_val = INVALID_DIR;
2311 
2312    /* 5. Get SE IMAP value. */
2313    if((e_ind < mw) && (s_ind < mh))
2314       se_val = *(imap + (s_ind*mw) + e_ind);
2315    else
2316       se_val = INVALID_DIR;
2317 
2318    /* 6. Get S IMAP value. */
2319    if(s_ind < mh)
2320       s_val = *(imap + (s_ind*mw) + mx);
2321    else
2322       s_val = INVALID_DIR;
2323 
2324    /* 7. Get SW IMAP value. */
2325    if((w_ind >= 0) && (s_ind < mh))
2326       sw_val = *(imap + (s_ind*mw) + w_ind);
2327    else
2328       sw_val = INVALID_DIR;
2329 
2330    /* 8. Get W IMAP value. */
2331    if(w_ind >= 0)
2332       w_val = *(imap + (my*mw) + w_ind);
2333    else
2334       w_val = INVALID_DIR;
2335 
2336    /* Now that we have all IMAP neighbors, determine largest change in */
2337    /* direction from current block to each of its 8 VALID neighbors.   */
2338 
2339    /* Initialize pointer to current IMAP value. */
2340    iptr = imap + (my*mw) + mx;
2341 
2342    /* Initialize curvature measure to negative as closest_dir_dist() */
2343    /* always returns -1=INVALID or a positive value.                 */
2344    cmeasure = -1;
2345 
2346    /* 1. With NW */
2347    /* Compute closest distance between neighboring directions. */
2348    dist = closest_dir_dist(*iptr, nw_val, ndirs);
2349    /* Keep track of maximum. */
2350    if(dist > cmeasure)
2351       cmeasure = dist;
2352 
2353    /* 2. With N */
2354    dist = closest_dir_dist(*iptr, n_val, ndirs);
2355    if(dist > cmeasure)
2356       cmeasure = dist;
2357 
2358    /* 3. With NE */
2359    dist = closest_dir_dist(*iptr, ne_val, ndirs);
2360    if(dist > cmeasure)
2361       cmeasure = dist;
2362 
2363    /* 4. With E */
2364    dist = closest_dir_dist(*iptr, e_val, ndirs);
2365    if(dist > cmeasure)
2366       cmeasure = dist;
2367 
2368    /* 5. With SE */
2369    dist = closest_dir_dist(*iptr, se_val, ndirs);
2370    if(dist > cmeasure)
2371       cmeasure = dist;
2372 
2373    /* 6. With S */
2374    dist = closest_dir_dist(*iptr, s_val, ndirs);
2375    if(dist > cmeasure)
2376       cmeasure = dist;
2377 
2378    /* 7. With SW */
2379    dist = closest_dir_dist(*iptr, sw_val, ndirs);
2380    if(dist > cmeasure)
2381       cmeasure = dist;
2382 
2383    /* 8. With W */
2384    dist = closest_dir_dist(*iptr, w_val, ndirs);
2385    if(dist > cmeasure)
2386       cmeasure = dist;
2387 
2388    /* Return maximum difference between current block's IMAP direction */
2389    /* and the rest of its VALID neighbors.                             */
2390    return(cmeasure);
2391 }
2392