maps.c 98 KB

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