minutia.c 153 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580
  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: MINUTIA.C
  40. AUTHOR: Michael D. Garris
  41. DATE: 05/11/1999
  42. UPDATED: 10/04/1999 Version 2 by MDG
  43. UPDATED: 09/13/2004
  44. Contains routines responsible for detecting initial minutia
  45. points as part of the NIST Latent Fingerprint System (LFS).
  46. ***********************************************************************
  47. ROUTINES:
  48. alloc_minutiae()
  49. realloc_minutiae()
  50. detect_minutiae()
  51. detect_minutiae_V2()
  52. update_minutiae()
  53. update_minutiae_V2()
  54. sort_minutiae_y_x()
  55. sort_minutiae_x_y()
  56. rm_dup_minutiae()
  57. dump_minutiae()
  58. dump_minutiae_pts()
  59. dump_reliable_minutiae_pts()
  60. create_minutia()
  61. free_minutiae()
  62. free_minutia()
  63. remove_minutia()
  64. join_minutia()
  65. minutia_type()
  66. is_minutia_appearing()
  67. choose_scan_direction()
  68. scan4minutiae()
  69. scan4minutiae_horizontally()
  70. scan4minutiae_horizontally_V2()
  71. scan4minutiae_vertically()
  72. scan4minutiae_vertically_V2()
  73. rescan4minutiae_horizontally()
  74. rescan4minutiae_vertically()
  75. rescan_partial_horizontally()
  76. rescan_partial_vertically()
  77. get_nbr_block_index()
  78. adjust_horizontal_rescan()
  79. adjust_vertical_rescan()
  80. process_horizontal_scan_minutia()
  81. process_horizontal_scan_minutia_V2()
  82. process_vertical_scan_minutia()
  83. process_vertical_scan_minutia_V2()
  84. adjust_high_curvature_minutia()
  85. adjust_high_curvature_minutia_V2()
  86. get_low_curvature_direction()
  87. ***********************************************************************/
  88. #include <stdio.h>
  89. #include <stdlib.h>
  90. #include "lfs.h"
  91. /*************************************************************************
  92. **************************************************************************
  93. #cat: alloc_minutiae - Allocates and initializes a minutia list based on the
  94. #cat: specified maximum number of minutiae to be detected.
  95. Input:
  96. max_minutiae - number of minutia to be allocated in list
  97. Output:
  98. ominutiae - points to the allocated minutiae list
  99. Return Code:
  100. Zero - successful completion
  101. Negative - system error
  102. **************************************************************************/
  103. int alloc_minutiae(MINUTIAE **ominutiae, const int max_minutiae)
  104. {
  105. MINUTIAE *minutiae;
  106. minutiae = (MINUTIAE *)malloc(sizeof(MINUTIAE));
  107. if(minutiae == (MINUTIAE *)NULL){
  108. fprintf(stderr, "ERROR : alloc_minutiae : malloc : minutiae\n");
  109. exit(-430);
  110. }
  111. minutiae->list = (MINUTIA **)malloc(max_minutiae * sizeof(MINUTIA *));
  112. if(minutiae->list == (MINUTIA **)NULL){
  113. fprintf(stderr, "ERROR : alloc_minutiae : malloc : minutiae->list\n");
  114. exit(-431);
  115. }
  116. minutiae->alloc = max_minutiae;
  117. minutiae->num = 0;
  118. *ominutiae = minutiae;
  119. return(0);
  120. }
  121. /*************************************************************************
  122. **************************************************************************
  123. #cat: realloc_minutiae - Reallocates a previously allocated minutia list
  124. #cat: extending its allocated length based on the specified
  125. #cat: increment.
  126. Input:
  127. minutiae - previously allocated list of minutiae points
  128. max_minutiae - number of minutia to be allocated in list
  129. Output:
  130. minutiae - extended list of minutiae points
  131. Return Code:
  132. Zero - successful completion
  133. Negative - system error
  134. **************************************************************************/
  135. int realloc_minutiae(MINUTIAE *minutiae, const int incr_minutiae)
  136. {
  137. minutiae->alloc += incr_minutiae;
  138. minutiae->list = (MINUTIA **)realloc(minutiae->list,
  139. minutiae->alloc * sizeof(MINUTIA *));
  140. if(minutiae->list == (MINUTIA **)NULL){
  141. fprintf(stderr, "ERROR : realloc_minutiae : realloc : minutiae->list\n");
  142. exit(-432);
  143. }
  144. return(0);
  145. }
  146. /*************************************************************************
  147. **************************************************************************
  148. #cat: detect_minutiae - Takes a binary image and its associated IMAP and
  149. #cat: NMAP matrices and scans each image block for potential
  150. #cat: minutia points.
  151. Input:
  152. bdata - binary image data (0==while & 1==black)
  153. iw - width (in pixels) of image
  154. ih - height (in pixels) of image
  155. imap - matrix of ridge flow directions
  156. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  157. blocks which have no neighboring valid directions.
  158. mw - width (in blocks) of IMAP and NMAP matrices.
  159. mh - height (in blocks) of IMAP and NMAP matrices.
  160. lfsparms - parameters and thresholds for controlling LFS
  161. Output:
  162. minutiae - points to a list of detected minutia structures
  163. Return Code:
  164. Zero - successful completion
  165. Negative - system error
  166. **************************************************************************/
  167. int detect_minutiae(MINUTIAE *minutiae,
  168. unsigned char *bdata, const int iw, const int ih,
  169. const int *imap, const int *nmap, const int mw, const int mh,
  170. const LFSPARMS *lfsparms)
  171. {
  172. int blk_i, blk_x, blk_y;
  173. int scan_x, scan_y, scan_w, scan_h;
  174. int scan_dir;
  175. int ret;
  176. /* Start with first block in IMAP. */
  177. blk_i = 0;
  178. /* Start with first scan line in image. */
  179. scan_y = 0;
  180. /* Foreach row of blocks in IMAP... */
  181. for(blk_y = 0; blk_y < mh; blk_y++){
  182. /* Reset to beginning of new block row. */
  183. scan_x = 0;
  184. /* Foreach block in current IMAP row... */
  185. for(blk_x = 0; blk_x < mw; blk_x++){
  186. /* If IMAP is VALID ... */
  187. if(imap[blk_i] != INVALID_DIR){
  188. /* Choose the feature scan direction based on the block's */
  189. /* VALID IMAP direction. The scan direction will either */
  190. /* be HORIZONTAL or VERTICAL. */
  191. scan_dir = choose_scan_direction(imap[blk_i],
  192. lfsparms->num_directions);
  193. /* Set width of scan region. The image may not be an even */
  194. /* multiple of "blocksize" in width and height, so we must */
  195. /* account for this. */
  196. /* Bump right by "blocksize" pixels, but not beyond the */
  197. /* image boundary. */
  198. scan_w = min(scan_x+lfsparms->blocksize, iw);
  199. /* Make the resulting width relative to the region's starting */
  200. /* x-pixel column. */
  201. scan_w -= scan_x;
  202. /* Bump down by "blocksize" pixels, but not beyond the */
  203. /* image boundary. */
  204. scan_h = min(scan_y+lfsparms->blocksize, ih);
  205. /* Make the resulting height relative to the region's starting */
  206. /* y-pixel row. */
  207. scan_h -= scan_y;
  208. /* Scan the defined region for minutia features. */
  209. if((ret = scan4minutiae(minutiae, bdata, iw, ih,
  210. imap, nmap, blk_x, blk_y, mw, mh,
  211. scan_x, scan_y, scan_w, scan_h, scan_dir,
  212. lfsparms))){
  213. /* Return code may be: */
  214. /* 1. ret<0 (implying system error) */
  215. return(ret);
  216. }
  217. } /* Otherwise, IMAP is INVALID, so ignore the block. This seems */
  218. /* quite drastic! */
  219. /* Advance to the next IMAP block in the row in the image. */
  220. scan_x += lfsparms->blocksize;
  221. /* Advance to the next IMAP block in the row. */
  222. blk_i++;
  223. } /* End foreach blk_x */
  224. /* Advance to the next IMAP row in the image. */
  225. scan_y += lfsparms->blocksize;
  226. } /* End foreach blk_y */
  227. /* Return normally. */
  228. return(0);
  229. }
  230. /*************************************************************************
  231. **************************************************************************
  232. #cat: detect_minutiae_V2 - Takes a binary image and its associated
  233. #cat: Direction and Low Flow Maps and scans each image block
  234. #cat: with valid direction for minutia points. Minutia points
  235. #cat: detected in LOW FLOW blocks are set with lower reliability.
  236. Input:
  237. bdata - binary image data (0==while & 1==black)
  238. iw - width (in pixels) of image
  239. ih - height (in pixels) of image
  240. direction_map - map of image blocks containing directional ridge flow
  241. low_flow_map - map of image blocks flagged as LOW RIDGE FLOW
  242. high_curve_map - map of image blocks flagged as HIGH CURVATURE
  243. mw - width (in blocks) of the maps
  244. mh - height (in blocks) of the maps
  245. lfsparms - parameters and thresholds for controlling LFS
  246. Output:
  247. minutiae - points to a list of detected minutia structures
  248. Return Code:
  249. Zero - successful completion
  250. Negative - system error
  251. **************************************************************************/
  252. int detect_minutiae_V2(MINUTIAE *minutiae,
  253. unsigned char *bdata, const int iw, const int ih,
  254. int *direction_map, int *low_flow_map, int *high_curve_map,
  255. const int mw, const int mh,
  256. const LFSPARMS *lfsparms)
  257. {
  258. int ret;
  259. int *pdirection_map, *plow_flow_map, *phigh_curve_map;
  260. /* Pixelize the maps by assigning block values to individual pixels. */
  261. if((ret = pixelize_map(&pdirection_map, iw, ih, direction_map, mw, mh,
  262. lfsparms->blocksize))){
  263. return(ret);
  264. }
  265. if((ret = pixelize_map(&plow_flow_map, iw, ih, low_flow_map, mw, mh,
  266. lfsparms->blocksize))){
  267. free(pdirection_map);
  268. return(ret);
  269. }
  270. if((ret = pixelize_map(&phigh_curve_map, iw, ih, high_curve_map, mw, mh,
  271. lfsparms->blocksize))){
  272. free(pdirection_map);
  273. free(plow_flow_map);
  274. return(ret);
  275. }
  276. if((ret = scan4minutiae_horizontally_V2(minutiae, bdata, iw, ih,
  277. pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){
  278. free(pdirection_map);
  279. free(plow_flow_map);
  280. free(phigh_curve_map);
  281. return(ret);
  282. }
  283. if((ret = scan4minutiae_vertically_V2(minutiae, bdata, iw, ih,
  284. pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){
  285. free(pdirection_map);
  286. free(plow_flow_map);
  287. free(phigh_curve_map);
  288. return(ret);
  289. }
  290. /* Deallocate working memories. */
  291. free(pdirection_map);
  292. free(plow_flow_map);
  293. free(phigh_curve_map);
  294. /* Return normally. */
  295. return(0);
  296. }
  297. /*************************************************************************
  298. **************************************************************************
  299. #cat: update_minutiae - Takes a detected minutia point and (if it is not
  300. #cat: determined to already be in the minutiae list) adds it to
  301. #cat: the list.
  302. Input:
  303. minutia - minutia structure for detected point
  304. bdata - binary image data (0==while & 1==black)
  305. iw - width (in pixels) of image
  306. ih - height (in pixels) of image
  307. lfsparms - parameters and thresholds for controlling LFS
  308. Output:
  309. minutiae - points to a list of detected minutia structures
  310. Return Code:
  311. Zero - minutia added to successfully added to minutiae list
  312. IGNORE - minutia is to be ignored (already in the minutiae list)
  313. Negative - system error
  314. **************************************************************************/
  315. int update_minutiae(MINUTIAE *minutiae, MINUTIA *minutia,
  316. unsigned char *bdata, const int iw, const int ih,
  317. const LFSPARMS *lfsparms)
  318. {
  319. int i, ret, dy, dx, delta_dir;
  320. int qtr_ndirs, full_ndirs;
  321. /* Check to see if minutiae list is full ... if so, then extend */
  322. /* the length of the allocated list of minutia points. */
  323. if(minutiae->num >= minutiae->alloc){
  324. if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE)))
  325. return(ret);
  326. }
  327. /* Otherwise, there is still room for more minutia. */
  328. /* Compute quarter of possible directions in a semi-circle */
  329. /* (ie. 45 degrees). */
  330. qtr_ndirs = lfsparms->num_directions>>2;
  331. /* Compute number of directions in full circle. */
  332. full_ndirs = lfsparms->num_directions<<1;
  333. /* Is the minutiae list empty? */
  334. if(minutiae->num > 0){
  335. /* Foreach minutia stored in the list... */
  336. for(i = 0; i < minutiae->num; i++){
  337. /* If x distance between new minutia and current list minutia */
  338. /* are sufficiently close... */
  339. dx = abs(minutiae->list[i]->x - minutia->x);
  340. if(dx < lfsparms->max_minutia_delta){
  341. /* If y distance between new minutia and current list minutia */
  342. /* are sufficiently close... */
  343. dy = abs(minutiae->list[i]->y - minutia->y);
  344. if(dy < lfsparms->max_minutia_delta){
  345. /* If new minutia and current list minutia are same type... */
  346. if(minutiae->list[i]->type == minutia->type){
  347. /* Test to see if minutiae have similar directions. */
  348. /* Take minimum of computed inner and outer */
  349. /* direction differences. */
  350. delta_dir = abs(minutiae->list[i]->direction -
  351. minutia->direction);
  352. delta_dir = min(delta_dir, full_ndirs-delta_dir);
  353. /* If directional difference is <= 45 degrees... */
  354. if(delta_dir <= qtr_ndirs){
  355. /* If new minutia and current list minutia share */
  356. /* the same point... */
  357. if((dx==0) && (dy==0)){
  358. /* Then the minutiae match, so don't add the new one */
  359. /* to the list. */
  360. return(IGNORE);
  361. }
  362. /* Othewise, check if they share the same contour. */
  363. /* Start by searching "max_minutia_delta" steps */
  364. /* clockwise. */
  365. /* If new minutia point found on contour... */
  366. if(search_contour(minutia->x, minutia->y,
  367. lfsparms->max_minutia_delta,
  368. minutiae->list[i]->x, minutiae->list[i]->y,
  369. minutiae->list[i]->ex, minutiae->list[i]->ey,
  370. SCAN_CLOCKWISE, bdata, iw, ih)){
  371. /* Consider the new minutia to be the same as the */
  372. /* current list minutia, so don't add the new one */
  373. /* to the list. */
  374. return(IGNORE);
  375. }
  376. /* Now search "max_minutia_delta" steps counter- */
  377. /* clockwise along contour. */
  378. /* If new minutia point found on contour... */
  379. if(search_contour(minutia->x, minutia->y,
  380. lfsparms->max_minutia_delta,
  381. minutiae->list[i]->x, minutiae->list[i]->y,
  382. minutiae->list[i]->ex, minutiae->list[i]->ey,
  383. SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){
  384. /* Consider the new minutia to be the same as the */
  385. /* current list minutia, so don't add the new one */
  386. /* to the list. */
  387. return(IGNORE);
  388. }
  389. /* Otherwise, new minutia and current list minutia do */
  390. /* not share the same contour, so although they are */
  391. /* similar in type and location, treat them as 2 */
  392. /* different minutia. */
  393. } /* Otherwise, directions are too different. */
  394. } /* Otherwise, minutiae are different type. */
  395. } /* Otherwise, minutiae too far apart in Y. */
  396. } /* Otherwise, minutiae too far apart in X. */
  397. } /* End FOR minutia in list. */
  398. } /* Otherwise, minutiae list is empty. */
  399. /* Otherwise, assume new minutia is not in the list, so add it. */
  400. minutiae->list[minutiae->num] = minutia;
  401. (minutiae->num)++;
  402. /* New minutia was successfully added to the list. */
  403. /* Return normally. */
  404. return(0);
  405. }
  406. /*************************************************************************
  407. **************************************************************************
  408. #cat: update_minutiae_V2 - Takes a detected minutia point and (if it is not
  409. #cat: determined to already be in the minutiae list or the
  410. #cat: new point is determined to be "more compatible") adds
  411. #cat: it to the list.
  412. Input:
  413. minutia - minutia structure for detected point
  414. scan_dir - orientation of scan when minutia was detected
  415. dmapval - directional ridge flow of block minutia is in
  416. bdata - binary image data (0==while & 1==black)
  417. iw - width (in pixels) of image
  418. ih - height (in pixels) of image
  419. lfsparms - parameters and thresholds for controlling LFS
  420. Output:
  421. minutiae - points to a list of detected minutia structures
  422. Return Code:
  423. Zero - minutia added to successfully added to minutiae list
  424. IGNORE - minutia is to be ignored (already in the minutiae list)
  425. Negative - system error
  426. **************************************************************************/
  427. int update_minutiae_V2(MINUTIAE *minutiae, MINUTIA *minutia,
  428. const int scan_dir, const int dmapval,
  429. unsigned char *bdata, const int iw, const int ih,
  430. const LFSPARMS *lfsparms)
  431. {
  432. int i, ret, dy, dx, delta_dir;
  433. int qtr_ndirs, full_ndirs;
  434. int map_scan_dir;
  435. /* Check to see if minutiae list is full ... if so, then extend */
  436. /* the length of the allocated list of minutia points. */
  437. if(minutiae->num >= minutiae->alloc){
  438. if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE)))
  439. return(ret);
  440. }
  441. /* Otherwise, there is still room for more minutia. */
  442. /* Compute quarter of possible directions in a semi-circle */
  443. /* (ie. 45 degrees). */
  444. qtr_ndirs = lfsparms->num_directions>>2;
  445. /* Compute number of directions in full circle. */
  446. full_ndirs = lfsparms->num_directions<<1;
  447. /* Is the minutiae list empty? */
  448. if(minutiae->num > 0){
  449. /* Foreach minutia stored in the list (in reverse order) ... */
  450. for(i = minutiae->num-1; i >= 0; i--){
  451. /* If x distance between new minutia and current list minutia */
  452. /* are sufficiently close... */
  453. dx = abs(minutiae->list[i]->x - minutia->x);
  454. if(dx < lfsparms->max_minutia_delta){
  455. /* If y distance between new minutia and current list minutia */
  456. /* are sufficiently close... */
  457. dy = abs(minutiae->list[i]->y - minutia->y);
  458. if(dy < lfsparms->max_minutia_delta){
  459. /* If new minutia and current list minutia are same type... */
  460. if(minutiae->list[i]->type == minutia->type){
  461. /* Test to see if minutiae have similar directions. */
  462. /* Take minimum of computed inner and outer */
  463. /* direction differences. */
  464. delta_dir = abs(minutiae->list[i]->direction -
  465. minutia->direction);
  466. delta_dir = min(delta_dir, full_ndirs-delta_dir);
  467. /* If directional difference is <= 45 degrees... */
  468. if(delta_dir <= qtr_ndirs){
  469. /* If new minutia and current list minutia share */
  470. /* the same point... */
  471. if((dx==0) && (dy==0)){
  472. /* Then the minutiae match, so don't add the new one */
  473. /* to the list. */
  474. return(IGNORE);
  475. }
  476. /* Othewise, check if they share the same contour. */
  477. /* Start by searching "max_minutia_delta" steps */
  478. /* clockwise. */
  479. /* If new minutia point found on contour... */
  480. if(search_contour(minutia->x, minutia->y,
  481. lfsparms->max_minutia_delta,
  482. minutiae->list[i]->x, minutiae->list[i]->y,
  483. minutiae->list[i]->ex, minutiae->list[i]->ey,
  484. SCAN_CLOCKWISE, bdata, iw, ih) ||
  485. search_contour(minutia->x, minutia->y,
  486. lfsparms->max_minutia_delta,
  487. minutiae->list[i]->x, minutiae->list[i]->y,
  488. minutiae->list[i]->ex, minutiae->list[i]->ey,
  489. SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){
  490. /* If new minutia has VALID block direction ... */
  491. if(dmapval >= 0){
  492. /* Derive feature scan direction compatible */
  493. /* with VALID direction. */
  494. map_scan_dir = choose_scan_direction(dmapval,
  495. lfsparms->num_directions);
  496. /* If map scan direction compatible with scan */
  497. /* direction in which new minutia was found ... */
  498. if(map_scan_dir == scan_dir){
  499. /* Then choose the new minutia over the one */
  500. /* currently in the list. */
  501. if((ret = remove_minutia(i, minutiae))){
  502. return(ret);
  503. }
  504. /* Continue on ... */
  505. }
  506. else
  507. /* Othersize, scan directions not compatible...*/
  508. /* so choose to keep the current minutia in */
  509. /* the list and ignore the new one. */
  510. return(IGNORE);
  511. }
  512. else{
  513. /* Otherwise, no reason to believe new minutia */
  514. /* is any better than the current one in the list,*/
  515. /* so consider the new minutia to be the same as */
  516. /* the current list minutia, and don't add the new*/
  517. /* one to the list. */
  518. return(IGNORE);
  519. }
  520. }
  521. /* Otherwise, new minutia and current list minutia do */
  522. /* not share the same contour, so although they are */
  523. /* similar in type and location, treat them as 2 */
  524. /* different minutia. */
  525. } /* Otherwise, directions are too different. */
  526. } /* Otherwise, minutiae are different type. */
  527. } /* Otherwise, minutiae too far apart in Y. */
  528. } /* Otherwise, minutiae too far apart in X. */
  529. } /* End FOR minutia in list. */
  530. } /* Otherwise, minutiae list is empty. */
  531. /* Otherwise, assume new minutia is not in the list, or those that */
  532. /* were close neighbors were selectively removed, so add it. */
  533. minutiae->list[minutiae->num] = minutia;
  534. (minutiae->num)++;
  535. /* New minutia was successfully added to the list. */
  536. /* Return normally. */
  537. return(0);
  538. }
  539. /*************************************************************************
  540. **************************************************************************
  541. #cat: sort_minutiae_y_x - Takes a list of minutia points and sorts them
  542. #cat: top-to-bottom and then left-to-right.
  543. Input:
  544. minutiae - list of minutiae
  545. iw - width (in pixels) of image
  546. ih - height (in pixels) of image
  547. Output:
  548. minutiae - list of sorted minutiae
  549. Return Code:
  550. Zero - successful completion
  551. Negative - system error
  552. **************************************************************************/
  553. int sort_minutiae_y_x(MINUTIAE *minutiae, const int iw, const int ih)
  554. {
  555. int *ranks, *order;
  556. int i, ret;
  557. MINUTIA **newlist;
  558. /* Allocate a list of integers to hold 1-D image pixel offsets */
  559. /* for each of the 2-D minutia coordinate points. */
  560. ranks = (int *)malloc(minutiae->num * sizeof(int));
  561. if(ranks == (int *)NULL){
  562. fprintf(stderr, "ERROR : sort_minutiae_y_x : malloc : ranks\n");
  563. return(-310);
  564. }
  565. /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */
  566. for(i = 0; i < minutiae->num; i++)
  567. ranks[i] = (minutiae->list[i]->y * iw) + minutiae->list[i]->x;
  568. /* Get sorted order of minutiae. */
  569. if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){
  570. free(ranks);
  571. return(ret);
  572. }
  573. /* Allocate new MINUTIA list to hold sorted minutiae. */
  574. newlist = (MINUTIA **)malloc(minutiae->num * sizeof(MINUTIA *));
  575. if(newlist == (MINUTIA **)NULL){
  576. free(ranks);
  577. free(order);
  578. fprintf(stderr, "ERROR : sort_minutiae_y_x : malloc : newlist\n");
  579. return(-311);
  580. }
  581. /* Put minutia into sorted order in new list. */
  582. for(i = 0; i < minutiae->num; i++)
  583. newlist[i] = minutiae->list[order[i]];
  584. /* Deallocate non-sorted list of minutia pointers. */
  585. free(minutiae->list);
  586. /* Assign new sorted list of minutia to minutiae list. */
  587. minutiae->list = newlist;
  588. /* Free the working memories supporting the sort. */
  589. free(order);
  590. free(ranks);
  591. /* Return normally. */
  592. return(0);
  593. }
  594. /*************************************************************************
  595. **************************************************************************
  596. #cat: sort_minutiae_x_y - Takes a list of minutia points and sorts them
  597. #cat: left-to-right and then top-to-bottom.
  598. Input:
  599. minutiae - list of minutiae
  600. iw - width (in pixels) of image
  601. ih - height (in pixels) of image
  602. Output:
  603. minutiae - list of sorted minutiae
  604. Return Code:
  605. Zero - successful completion
  606. Negative - system error
  607. **************************************************************************/
  608. int sort_minutiae_x_y(MINUTIAE *minutiae, const int iw, const int ih)
  609. {
  610. int *ranks, *order;
  611. int i, ret;
  612. MINUTIA **newlist;
  613. /* Allocate a list of integers to hold 1-D image pixel offsets */
  614. /* for each of the 2-D minutia coordinate points. */
  615. ranks = (int *)malloc(minutiae->num * sizeof(int));
  616. if(ranks == (int *)NULL){
  617. fprintf(stderr, "ERROR : sort_minutiae_x_y : malloc : ranks\n");
  618. return(-440);
  619. }
  620. /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */
  621. for(i = 0; i < minutiae->num; i++)
  622. ranks[i] = (minutiae->list[i]->x * iw) + minutiae->list[i]->y;
  623. /* Get sorted order of minutiae. */
  624. if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){
  625. free(ranks);
  626. return(ret);
  627. }
  628. /* Allocate new MINUTIA list to hold sorted minutiae. */
  629. newlist = (MINUTIA **)malloc(minutiae->num * sizeof(MINUTIA *));
  630. if(newlist == (MINUTIA **)NULL){
  631. free(ranks);
  632. free(order);
  633. fprintf(stderr, "ERROR : sort_minutiae_x_y : malloc : newlist\n");
  634. return(-441);
  635. }
  636. /* Put minutia into sorted order in new list. */
  637. for(i = 0; i < minutiae->num; i++)
  638. newlist[i] = minutiae->list[order[i]];
  639. /* Deallocate non-sorted list of minutia pointers. */
  640. free(minutiae->list);
  641. /* Assign new sorted list of minutia to minutiae list. */
  642. minutiae->list = newlist;
  643. /* Free the working memories supporting the sort. */
  644. free(order);
  645. free(ranks);
  646. /* Return normally. */
  647. return(0);
  648. }
  649. /*************************************************************************
  650. **************************************************************************
  651. #cat: rm_dup_minutiae - Takes a list of minutiae sorted in some adjacent order
  652. #cat: and detects and removes redundant minutia that have the
  653. #cat: same exact pixel coordinate locations (even if other
  654. #cat: attributes may differ).
  655. Input:
  656. mintuiae - list of sorted minutiae
  657. Output:
  658. mintuiae - list of sorted minutiae with duplicates removed
  659. Return Code:
  660. Zero - successful completion
  661. Negative - system error
  662. **************************************************************************/
  663. int rm_dup_minutiae(MINUTIAE *minutiae)
  664. {
  665. int i, ret;
  666. MINUTIA *minutia1, *minutia2;
  667. /* Work backward from the end of the list of minutiae. This way */
  668. /* we can selectively remove minutia from the list and not cause */
  669. /* problems with keeping track of current indices. */
  670. for(i = minutiae->num-1; i > 0; i--){
  671. minutia1 = minutiae->list[i];
  672. minutia2 = minutiae->list[i-1];
  673. /* If minutia pair has identical coordinates ... */
  674. if((minutia1->x == minutia2->x) &&
  675. (minutia1->y == minutia2->y)){
  676. /* Remove the 2nd minutia from the minutiae list. */
  677. if((ret = remove_minutia(i-1, minutiae)))
  678. return(ret);
  679. /* The first minutia slides into the position of the 2nd. */
  680. }
  681. }
  682. /* Return successfully. */
  683. return(0);
  684. }
  685. /*************************************************************************
  686. **************************************************************************
  687. #cat: dump_minutiae - Given a minutiae list, writes a formatted text report of
  688. #cat: the list's contents to the specified open file pointer.
  689. Input:
  690. minutiae - list of minutia structures
  691. Output:
  692. fpout - open file pointer
  693. **************************************************************************/
  694. void dump_minutiae(FILE *fpout, const MINUTIAE *minutiae)
  695. {
  696. int i, j;
  697. fprintf(fpout, "\n%d Minutiae Detected\n\n", minutiae->num);
  698. for(i = 0; i < minutiae->num; i++){
  699. /* Precision of reliablity added one decimal position */
  700. /* on 09-13-04 */
  701. fprintf(fpout, "%4d : %4d, %4d : %2d : %6.3f :", i,
  702. minutiae->list[i]->x, minutiae->list[i]->y,
  703. minutiae->list[i]->direction, minutiae->list[i]->reliability);
  704. if(minutiae->list[i]->type == RIDGE_ENDING)
  705. fprintf(fpout, "RIG : ");
  706. else
  707. fprintf(fpout, "BIF : ");
  708. if(minutiae->list[i]->appearing)
  709. fprintf(fpout, "APP : ");
  710. else
  711. fprintf(fpout, "DIS : ");
  712. fprintf(fpout, "%2d ", minutiae->list[i]->feature_id);
  713. for(j = 0; j < minutiae->list[i]->num_nbrs; j++){
  714. fprintf(fpout, ": %4d,%4d; %2d ",
  715. minutiae->list[minutiae->list[i]->nbrs[j]]->x,
  716. minutiae->list[minutiae->list[i]->nbrs[j]]->y,
  717. minutiae->list[i]->ridge_counts[j]);
  718. }
  719. fprintf(fpout, "\n");
  720. }
  721. }
  722. /*************************************************************************
  723. **************************************************************************
  724. #cat: dump_minutiae_pts - Given a minutiae list, writes the coordinate point
  725. #cat: for each minutia in the list to the specified open
  726. #cat: file pointer.
  727. Input:
  728. minutiae - list of minutia structures
  729. Output:
  730. fpout - open file pointer
  731. **************************************************************************/
  732. void dump_minutiae_pts(FILE *fpout, const MINUTIAE *minutiae)
  733. {
  734. int i;
  735. /* First line in the output file contians the number of minutia */
  736. /* points to be written to the file. */
  737. fprintf(fpout, "%d\n", minutiae->num);
  738. /* Foreach minutia in list... */
  739. for(i = 0; i < minutiae->num; i++){
  740. /* Write the minutia's coordinate point to the file pointer. */
  741. fprintf(fpout, "%4d %4d\n", minutiae->list[i]->x, minutiae->list[i]->y);
  742. }
  743. }
  744. /*************************************************************************
  745. **************************************************************************
  746. #cat: dump_reliable_minutiae_pts - Given a minutiae list, writes the
  747. #cat: coordinate point for each minutia in the list that has
  748. #cat: the specified reliability to the specified open
  749. #cat: file pointer.
  750. Input:
  751. minutiae - list of minutia structures
  752. reliability - desired reliability level for minutiae to be reported
  753. Output:
  754. fpout - open file pointer
  755. **************************************************************************/
  756. void dump_reliable_minutiae_pts(FILE *fpout, const MINUTIAE *minutiae,
  757. const double reliability)
  758. {
  759. int i, count;
  760. /* First count the number of qualifying minutiae so that the */
  761. /* MFS header may be written. */
  762. count = 0;
  763. /* Foreach minutia in list... */
  764. for(i = 0; i < minutiae->num; i++){
  765. if(minutiae->list[i]->reliability == reliability)
  766. count++;
  767. }
  768. /* First line in the output file contians the number of minutia */
  769. /* points to be written to the file. */
  770. fprintf(fpout, "%d\n", count);
  771. /* Foreach minutia in list... */
  772. for(i = 0; i < minutiae->num; i++){
  773. if(minutiae->list[i]->reliability == reliability)
  774. /* Write the minutia's coordinate point to the file pointer. */
  775. fprintf(fpout, "%4d %4d\n",
  776. minutiae->list[i]->x, minutiae->list[i]->y);
  777. }
  778. }
  779. /*************************************************************************
  780. **************************************************************************
  781. #cat: create_minutia - Takes attributes associated with a detected minutia
  782. #cat: point and allocates and initializes a minutia structure.
  783. Input:
  784. x_loc - x-pixel coord of minutia (interior to feature)
  785. y_loc - y-pixel coord of minutia (interior to feature)
  786. x_edge - x-pixel coord of corresponding edge pixel (exterior to feature)
  787. y_edge - y-pixel coord of corresponding edge pixel (exterior to feature)
  788. idir - integer direction of the minutia
  789. reliability - floating point measure of minutia's reliability
  790. type - type of the minutia (ridge-ending or bifurcation)
  791. appearing - designates the minutia as appearing or disappearing
  792. feature_id - index of minutia's matching feature_patterns[]
  793. Output:
  794. ominutia - ponter to an allocated and initialized minutia structure
  795. Return Code:
  796. Zero - minutia structure successfully allocated and initialized
  797. Negative - system error
  798. *************************************************************************/
  799. int create_minutia(MINUTIA **ominutia, const int x_loc, const int y_loc,
  800. const int x_edge, const int y_edge, const int idir,
  801. const double reliability,
  802. const int type, const int appearing, const int feature_id)
  803. {
  804. MINUTIA *minutia;
  805. /* Allocate a minutia structure. */
  806. minutia = (MINUTIA *)malloc(sizeof(MINUTIA));
  807. /* If allocation error... */
  808. if(minutia == (MINUTIA *)NULL){
  809. fprintf(stderr, "ERROR : create_minutia : malloc : minutia\n");
  810. return(-230);
  811. }
  812. /* Assign minutia structure attributes. */
  813. minutia->x = x_loc;
  814. minutia->y = y_loc;
  815. minutia->ex = x_edge;
  816. minutia->ey = y_edge;
  817. minutia->direction = idir;
  818. minutia->reliability = reliability;
  819. minutia->type = type;
  820. minutia->appearing = appearing;
  821. minutia->feature_id = feature_id;
  822. minutia->nbrs = (int *)NULL;
  823. minutia->ridge_counts = (int *)NULL;
  824. minutia->num_nbrs = 0;
  825. /* Set minutia object to output pointer. */
  826. *ominutia = minutia;
  827. /* Return normally. */
  828. return(0);
  829. }
  830. /*************************************************************************
  831. **************************************************************************
  832. #cat: free_minutiae - Takes a minutiae list and deallocates all memory
  833. #cat: associated with it.
  834. Input:
  835. minutiae - pointer to allocated list of minutia structures
  836. *************************************************************************/
  837. void free_minutiae(MINUTIAE *minutiae)
  838. {
  839. int i;
  840. /* Deallocate minutia structures in the list. */
  841. for(i = 0; i < minutiae->num; i++)
  842. free_minutia(minutiae->list[i]);
  843. /* Deallocate list of minutia pointers. */
  844. free(minutiae->list);
  845. /* Deallocate the list structure. */
  846. free(minutiae);
  847. }
  848. /*************************************************************************
  849. **************************************************************************
  850. #cat: free_minutia - Takes a minutia pointer and deallocates all memory
  851. #cat: associated with it.
  852. Input:
  853. minutia - pointer to allocated minutia structure
  854. *************************************************************************/
  855. void free_minutia(MINUTIA *minutia)
  856. {
  857. /* Deallocate sublists. */
  858. if(minutia->nbrs != (int *)NULL)
  859. free(minutia->nbrs);
  860. if(minutia->ridge_counts != (int *)NULL)
  861. free(minutia->ridge_counts);
  862. /* Deallocate the minutia structure. */
  863. free(minutia);
  864. }
  865. /*************************************************************************
  866. **************************************************************************
  867. #cat: remove_minutia - Removes the specified minutia point from the input
  868. #cat: list of minutiae.
  869. Input:
  870. index - position of minutia to be removed from list
  871. minutiae - input list of minutiae
  872. Output:
  873. minutiae - list with minutia removed
  874. Return Code:
  875. Zero - successful completion
  876. Negative - system error
  877. **************************************************************************/
  878. int remove_minutia(const int index, MINUTIAE *minutiae)
  879. {
  880. int fr, to;
  881. /* Make sure the requested index is within range. */
  882. if((index < 0) && (index >= minutiae->num)){
  883. fprintf(stderr, "ERROR : remove_minutia : index out of range\n");
  884. return(-380);
  885. }
  886. /* Deallocate the minutia structure to be removed. */
  887. free_minutia(minutiae->list[index]);
  888. /* Slide the remaining list of minutiae up over top of the */
  889. /* position of the minutia being removed. */
  890. for(to = index, fr = index+1; fr < minutiae->num; to++, fr++)
  891. minutiae->list[to] = minutiae->list[fr];
  892. /* Decrement the number of minutiae remaining in the list. */
  893. minutiae->num--;
  894. /* Return normally. */
  895. return(0);
  896. }
  897. /*************************************************************************
  898. **************************************************************************
  899. #cat: join_minutia - Takes 2 minutia points and connectes their features in
  900. #cat: the input binary image. A line is drawn in the image
  901. #cat: between the 2 minutia with a specified line-width radius
  902. #cat: and a conditional border of pixels opposite in color
  903. #cat: from the interior line.
  904. Input:
  905. minutia1 - first minutia point to be joined
  906. minutia2 - second minutia point to be joined
  907. bdata - binary image data (0==while & 1==black)
  908. iw - width (in pixels) of image
  909. ih - height (in pixels) of image
  910. with_boundary - signifies the inclusion of border pixels
  911. line_radius - line-width radius of join line
  912. Output:
  913. bdata - edited image with minutia features joined
  914. Return Code:
  915. Zero - successful completion
  916. Negative - system error
  917. **************************************************************************/
  918. int join_minutia(const MINUTIA *minutia1, const MINUTIA *minutia2,
  919. unsigned char *bdata, const int iw, const int ih,
  920. const int with_boundary, const int line_radius)
  921. {
  922. int dx_gte_dy, delta_x, delta_y;
  923. int *x_list, *y_list, num;
  924. int minutia_pix, boundary_pix;
  925. int i, j, ret;
  926. int x1, y1, x2, y2;
  927. /* Compute X and Y deltas between minutia points. */
  928. delta_x = abs(minutia1->x - minutia2->x);
  929. delta_y = abs(minutia1->y - minutia2->y);
  930. /* Set flag based on |DX| >= |DY|. */
  931. /* If flag is true then add additional pixel width to the join line */
  932. /* by adding pixels neighboring top and bottom. */
  933. /* If flag is false then add additional pixel width to the join line */
  934. /* by adding pixels neighboring left and right. */
  935. if(delta_x >= delta_y)
  936. dx_gte_dy = 1;
  937. else
  938. dx_gte_dy = 0;
  939. /* Compute points along line segment between the two minutia points. */
  940. if((ret = line_points(&x_list, &y_list, &num,
  941. minutia1->x, minutia1->y, minutia2->x, minutia2->y)))
  942. /* If error with line routine, return error code. */
  943. return(ret);
  944. /* Determine pixel color of minutia and boundary. */
  945. if(minutia1->type == RIDGE_ENDING){
  946. /* To connect 2 ridge-endings, draw black. */
  947. minutia_pix = 1;
  948. boundary_pix = 0;
  949. }
  950. else{
  951. /* To connect 2 bifurcations, draw white. */
  952. minutia_pix = 0;
  953. boundary_pix = 1;
  954. }
  955. /* Foreach point on line connecting the minutiae points ... */
  956. for(i = 1; i < num-1; i++){
  957. /* Draw minutia pixel at current point on line. */
  958. *(bdata+(y_list[i]*iw)+x_list[i]) = minutia_pix;
  959. /* Initialize starting corrdinates for adding width to the */
  960. /* join line to the current point on the line. */
  961. x1 = x_list[i];
  962. y1 = y_list[i];
  963. x2 = x1;
  964. y2 = y1;
  965. /* Foreach pixel of added radial width ... */
  966. for(j = 0; j < line_radius; j++){
  967. /* If |DX|>=|DY|, we want to add width to line by writing */
  968. /* to pixels neighboring above and below. */
  969. /* x1 -= (0=(1-1)); y1 -= 1 ==> ABOVE */
  970. /* x2 += (0=(1-1)); y2 += 1 ==> BELOW */
  971. /* If |DX|<|DY|, we want to add width to line by writing */
  972. /* to pixels neighboring left and right. */
  973. /* x1 -= (1=(1-0)); y1 -= 0 ==> LEFT */
  974. /* x2 += (1=(1-0)); y2 += 0 ==> RIGHT */
  975. /* Advance 1st point along width dimension. */
  976. x1 -= (1 - dx_gte_dy);
  977. y1 -= dx_gte_dy;
  978. /* If pixel 1st point is within image boundaries ... */
  979. if((x1 >= 0) && (x1 < iw) &&
  980. (y1 >= 0) && (y1 < ih))
  981. /* Write the pixel ABOVE or LEFT. */
  982. *(bdata+(y1*iw)+x1) = minutia_pix;
  983. /* Advance 2nd point along width dimension. */
  984. x2 += (1 - dx_gte_dy);
  985. y2 += dx_gte_dy;
  986. /* If pixel 2nd point is within image boundaries ... */
  987. if((x2 >= 0) && (x2 < iw) &&
  988. /* Write the pixel BELOW or RIGHT. */
  989. (y2 >= 0) && (y2 < ih))
  990. *(bdata+(y2*iw)+x2) = minutia_pix;
  991. }
  992. /* If boundary flag is set ... draw the boundary pixels.*/
  993. if(with_boundary){
  994. /* Advance 1st point along width dimension. */
  995. x1 -= (1 - dx_gte_dy);
  996. y1 -= dx_gte_dy;
  997. /* If pixel 1st point is within image boundaries ... */
  998. if((x1 >= 0) && (x1 < iw) &&
  999. (y1 >= 0) && (y1 < ih))
  1000. /* Write the pixel ABOVE or LEFT of opposite color. */
  1001. *(bdata+(y1*iw)+x1) = boundary_pix;
  1002. /* Advance 2nd point along width dimension. */
  1003. x2 += (1 - dx_gte_dy);
  1004. y2 += dx_gte_dy;
  1005. /* If pixel 2nd point is within image boundaries ... */
  1006. if((x2 >= 0) && (x2 < iw) &&
  1007. (y2 >= 0) && (y2 < ih))
  1008. /* Write the pixel BELOW or RIGHT of opposite color. */
  1009. *(bdata+(y2*iw)+x2) = boundary_pix;
  1010. }
  1011. }
  1012. /* Deallocate points along connecting line. */
  1013. free(x_list);
  1014. free(y_list);
  1015. /* Return normally. */
  1016. return(0);
  1017. }
  1018. /*************************************************************************
  1019. **************************************************************************
  1020. #cat: minutia_type - Given the pixel color of the detected feature, returns
  1021. #cat: whether the minutia is a ridge-ending (black pixel) or
  1022. #cat: bifurcation (white pixel).
  1023. Input:
  1024. feature_pix - pixel color of the feature's interior
  1025. Return Code:
  1026. RIDGE_ENDING - minutia is a ridge-ending
  1027. BIFURCATION - minutia is a bifurcation (valley-ending)
  1028. **************************************************************************/
  1029. int minutia_type(const int feature_pix)
  1030. {
  1031. int type;
  1032. /* If feature pixel is white ... */
  1033. if(feature_pix == 0)
  1034. /* Then the feature is a valley-ending, so BIFURCATION. */
  1035. type = BIFURCATION;
  1036. /* Otherwise, the feature pixel is black ... */
  1037. else
  1038. /* So the feature is a RIDGE-ENDING. */
  1039. type = RIDGE_ENDING;
  1040. /* Return the type. */
  1041. return(type);
  1042. }
  1043. /*************************************************************************
  1044. **************************************************************************
  1045. #cat: is_minutia_appearing - Given the pixel location of a minutia feature
  1046. #cat: and its corresponding adjacent edge pixel, returns whether
  1047. #cat: the minutia is appearing or disappearing. Remeber, that
  1048. #cat: "feature" refers to either a ridge or valley-ending.
  1049. Input:
  1050. x_loc - x-pixel coord of feature (interior to feature)
  1051. y_loc - y-pixel coord of feature (interior to feature)
  1052. x_edge - x-pixel coord of corresponding edge pixel
  1053. (exterior to feature)
  1054. y_edge - y-pixel coord of corresponding edge pixel
  1055. (exterior to feature)
  1056. Return Code:
  1057. APPEARING - minutia is appearing (TRUE==1)
  1058. DISAPPEARING - minutia is disappearing (FALSE==0)
  1059. Negative - system error
  1060. **************************************************************************/
  1061. int is_minutia_appearing(const int x_loc, const int y_loc,
  1062. const int x_edge, const int y_edge)
  1063. {
  1064. /* Edge pixels will always be N,S,E,W of feature pixel. */
  1065. /* 1. When scanning for feature's HORIZONTALLY... */
  1066. /* If the edge is above the feature, then appearing. */
  1067. if(x_edge < x_loc)
  1068. return(APPEARING);
  1069. /* If the edge is below the feature, then disappearing. */
  1070. if(x_edge > x_loc)
  1071. return(DISAPPEARING);
  1072. /* 1. When scanning for feature's VERTICALLY... */
  1073. /* If the edge is left of feature, then appearing. */
  1074. if(y_edge < y_loc)
  1075. return(APPEARING);
  1076. /* If the edge is right of feature, then disappearing. */
  1077. if(y_edge > y_loc)
  1078. return(DISAPPEARING);
  1079. /* Should never get here, but just in case. */
  1080. fprintf(stderr,
  1081. "ERROR : is_minutia_appearing : bad configuration of pixels\n");
  1082. return(-240);
  1083. }
  1084. /*************************************************************************
  1085. **************************************************************************
  1086. #cat: choose_scan_direction - Determines the orientation (horizontal or
  1087. #cat: vertical) in which a block is to be scanned for minutiae.
  1088. #cat: The orientation is based on the blocks corresponding IMAP
  1089. #cat: direction.
  1090. Input:
  1091. imapval - Block's IMAP direction
  1092. ndirs - number of possible IMAP directions (within semicircle)
  1093. Return Code:
  1094. SCAN_HORIZONTAL - horizontal orientation
  1095. SCAN_VERTICAL - vertical orientation
  1096. **************************************************************************/
  1097. int choose_scan_direction(const int imapval, const int ndirs)
  1098. {
  1099. int qtr_ndirs;
  1100. /* Compute quarter of directions in semi-circle. */
  1101. qtr_ndirs = ndirs>>2;
  1102. /* If ridge flow in block is relatively vertical, then we want */
  1103. /* to scan for minutia features in the opposite direction */
  1104. /* (ie. HORIZONTALLY). */
  1105. if((imapval <= qtr_ndirs) || (imapval > (qtr_ndirs*3)))
  1106. return(SCAN_HORIZONTAL);
  1107. /* Otherwise, ridge flow is realtively horizontal, and we want */
  1108. /* to scan for minutia features in the opposite direction */
  1109. /* (ie. VERTICALLY). */
  1110. else
  1111. return(SCAN_VERTICAL);
  1112. }
  1113. /*************************************************************************
  1114. **************************************************************************
  1115. #cat: scan4minutiae - Scans a block of binary image data detecting potential
  1116. #cat: minutiae points.
  1117. Input:
  1118. bdata - binary image data (0==while & 1==black)
  1119. iw - width (in pixels) of image
  1120. ih - height (in pixels) of image
  1121. imap - matrix of ridge flow directions
  1122. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  1123. blocks which have no neighboring valid directions.
  1124. blk_x - x-block coord to be scanned
  1125. blk_y - y-block coord to be scanned
  1126. mw - width (in blocks) of IMAP and NMAP matrices.
  1127. mh - height (in blocks) of IMAP and NMAP matrices.
  1128. scan_x - x-pixel coord of origin of region to be scanned
  1129. scan_y - y-pixel coord of origin of region to be scanned
  1130. scan_w - width (in pixels) of region to be scanned
  1131. scan_h - height (in pixels) of region to be scanned
  1132. scan_dir - the scan orientation (horizontal or vertical)
  1133. lfsparms - parameters and thresholds for controlling LFS
  1134. Output:
  1135. minutiae - points to a list of detected minutia structures
  1136. Return Code:
  1137. Zero - successful completion
  1138. Negative - system error
  1139. **************************************************************************/
  1140. int scan4minutiae(MINUTIAE *minutiae,
  1141. unsigned char *bdata, const int iw, const int ih,
  1142. const int *imap, const int *nmap,
  1143. const int blk_x, const int blk_y, const int mw, const int mh,
  1144. const int scan_x, const int scan_y,
  1145. const int scan_w, const int scan_h, const int scan_dir,
  1146. const LFSPARMS *lfsparms)
  1147. {
  1148. int blk_i, ret;
  1149. /* Compute block index from block coordinates. */
  1150. blk_i = (blk_y*mw) + blk_x;
  1151. /* Conduct primary scan for minutiae horizontally. */
  1152. if(scan_dir == SCAN_HORIZONTAL){
  1153. if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
  1154. imap[blk_i], nmap[blk_i],
  1155. scan_x, scan_y, scan_w, scan_h, lfsparms))){
  1156. /* Return code may be: */
  1157. /* 1. ret<0 (implying system error) */
  1158. return(ret);
  1159. }
  1160. /* Rescan block vertically. */
  1161. if((ret = rescan4minutiae_vertically(minutiae, bdata, iw, ih,
  1162. imap, nmap, blk_x, blk_y, mw, mh,
  1163. scan_x, scan_y, scan_w, scan_h, lfsparms))){
  1164. /* Return code may be: */
  1165. /* 1. ret<0 (implying system error) */
  1166. return(ret);
  1167. }
  1168. }
  1169. /* Otherwise, conduct primary scan for minutiae vertically. */
  1170. else{
  1171. if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
  1172. imap[blk_i], nmap[blk_i],
  1173. scan_x, scan_y, scan_w, scan_h, lfsparms))){
  1174. /* Return resulting code. */
  1175. return(ret);
  1176. }
  1177. /* Rescan block horizontally. */
  1178. if((ret = rescan4minutiae_horizontally(minutiae, bdata, iw, ih,
  1179. imap, nmap, blk_x, blk_y, mw, mh,
  1180. scan_x, scan_y, scan_w, scan_h, lfsparms))){
  1181. /* Return resulting code. */
  1182. return(ret);
  1183. }
  1184. }
  1185. /* Return normally. */
  1186. return(0);
  1187. }
  1188. /*************************************************************************
  1189. **************************************************************************
  1190. #cat: scan4minutiae_horizontally - Scans a specified region of binary image
  1191. #cat: data horizontally, detecting potential minutiae points.
  1192. #cat: Minutia detected via the horizontal scan process are
  1193. #cat: by nature vertically oriented (orthogonal to the scan).
  1194. #cat: The region actually scanned is slightly larger than that
  1195. #cat: specified. This overlap attempts to minimize the number
  1196. #cat: of minutiae missed at the region boundaries.
  1197. #cat: HOWEVER, some minutiae will still be missed!
  1198. Input:
  1199. bdata - binary image data (0==while & 1==black)
  1200. iw - width (in pixels) of image
  1201. ih - height (in pixels) of image
  1202. imapval - IMAP value associated with this image region
  1203. nmapval - NMAP value associated with this image region
  1204. scan_x - x-pixel coord of origin of region to be scanned
  1205. scan_y - y-pixel coord of origin of region to be scanned
  1206. scan_w - width (in pixels) of region to be scanned
  1207. scan_h - height (in pixels) of region to be scanned
  1208. lfsparms - parameters and thresholds for controlling LFS
  1209. Output:
  1210. minutiae - points to a list of detected minutia structures
  1211. Return Code:
  1212. Zero - successful completion
  1213. Negative - system error
  1214. **************************************************************************/
  1215. int scan4minutiae_horizontally(MINUTIAE *minutiae,
  1216. unsigned char *bdata, const int iw, const int ih,
  1217. const int imapval, const int nmapval,
  1218. const int scan_x, const int scan_y,
  1219. const int scan_w, const int scan_h,
  1220. const LFSPARMS *lfsparms)
  1221. {
  1222. int sx, sy, ex, ey, cx, cy, x2;
  1223. unsigned char *p1ptr, *p2ptr;
  1224. int possible[NFEATURES], nposs;
  1225. int ret;
  1226. /* NOTE!!! Minutia that "straddle" region boundaries may be missed! */
  1227. /* If possible, overlap left and right of current scan region */
  1228. /* by 2 pixel columns to help catch some minutia that straddle the */
  1229. /* the scan region boundaries. */
  1230. sx = max(0, scan_x-2);
  1231. ex = min(iw, scan_x+scan_w+2);
  1232. /* If possible, overlap the scan region below by 1 pixel row. */
  1233. sy = scan_y;
  1234. ey = min(ih, scan_y+scan_h+1);
  1235. /* For now, we will not adjust for IMAP edge, as the binary image */
  1236. /* was properly padded at its edges so as not to cause anomallies. */
  1237. /* Start at first row in region. */
  1238. cy = sy;
  1239. /* While second scan row not outside the bottom of the scan region... */
  1240. while(cy+1 < ey){
  1241. /* Start at beginning of new scan row in region. */
  1242. cx = sx;
  1243. /* While not at end of region's current scan row. */
  1244. while(cx < ex){
  1245. /* Get pixel pair from current x position in current and next */
  1246. /* scan rows. */
  1247. p1ptr = bdata+(cy*iw)+cx;
  1248. p2ptr = bdata+((cy+1)*iw)+cx;
  1249. /* If scan pixel pair matches first pixel pair of */
  1250. /* 1 or more features... */
  1251. if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1252. /* Bump forward to next scan pixel pair. */
  1253. cx++;
  1254. p1ptr++;
  1255. p2ptr++;
  1256. /* If not at end of region's current scan row... */
  1257. if(cx < ex){
  1258. /* If scan pixel pair matches second pixel pair of */
  1259. /* 1 or more features... */
  1260. if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1261. /* Store current x location. */
  1262. x2 = cx;
  1263. /* Skip repeated pixel pairs. */
  1264. skip_repeated_horizontal_pair(&cx, ex, &p1ptr, &p2ptr,
  1265. iw, ih);
  1266. /* If not at end of region's current scan row... */
  1267. if(cx < ex){
  1268. /* If scan pixel pair matches third pixel pair of */
  1269. /* a single feature... */
  1270. if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1271. /* Process detected minutia point. */
  1272. if((ret = process_horizontal_scan_minutia(minutiae,
  1273. cx, cy, x2, possible[0],
  1274. bdata, iw, ih,
  1275. imapval, nmapval, lfsparms))){
  1276. /* Return code may be: */
  1277. /* 1. ret< 0 (implying system error) */
  1278. /* 2. ret==IGNORE (ignore current feature) */
  1279. if(ret < 0)
  1280. return(ret);
  1281. /* Otherwise, IGNORE and continue. */
  1282. }
  1283. }
  1284. /* Set up to resume scan. */
  1285. /* Test to see if 3rd pair can slide into 2nd pair. */
  1286. /* The values of the 2nd pair MUST be different. */
  1287. /* If 3rd pair values are different ... */
  1288. if(*p1ptr != *p2ptr){
  1289. /* Set next first pair to last of repeated */
  1290. /* 2nd pairs, ie. back up one pair. */
  1291. cx--;
  1292. }
  1293. /* Otherwise, 3rd pair can't be a 2nd pair, so */
  1294. /* keep pointing to 3rd pair so that it is used */
  1295. /* in the next first pair test. */
  1296. } /* Else, at end of current scan row. */
  1297. }
  1298. /* Otherwise, 2nd pair failed, so keep pointing to it */
  1299. /* so that it is used in the next first pair test. */
  1300. } /* Else, at end of current scan row. */
  1301. }
  1302. /* Otherwise, 1st pair failed... */
  1303. else{
  1304. /* Bump forward to next pixel pair. */
  1305. cx++;
  1306. }
  1307. } /* While not at end of current scan row. */
  1308. /* Bump forward to next scan row. */
  1309. cy++;
  1310. } /* While not out of scan rows. */
  1311. /* Return normally. */
  1312. return(0);
  1313. }
  1314. /*************************************************************************
  1315. **************************************************************************
  1316. #cat: scan4minutiae_horizontally_V2 - Scans an entire binary image
  1317. #cat: horizontally, detecting potential minutiae points.
  1318. #cat: Minutia detected via the horizontal scan process are
  1319. #cat: by nature vertically oriented (orthogonal to the scan).
  1320. Input:
  1321. bdata - binary image data (0==while & 1==black)
  1322. iw - width (in pixels) of image
  1323. ih - height (in pixels) of image
  1324. pdirection_map - pixelized Direction Map
  1325. plow_flow_map - pixelized Low Ridge Flow Map
  1326. phigh_curve_map - pixelized High Curvature Map
  1327. lfsparms - parameters and thresholds for controlling LFS
  1328. Output:
  1329. minutiae - points to a list of detected minutia structures
  1330. Return Code:
  1331. Zero - successful completion
  1332. Negative - system error
  1333. **************************************************************************/
  1334. int scan4minutiae_horizontally_V2(MINUTIAE *minutiae,
  1335. unsigned char *bdata, const int iw, const int ih,
  1336. int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
  1337. const LFSPARMS *lfsparms)
  1338. {
  1339. int sx, sy, ex, ey, cx, cy, x2;
  1340. unsigned char *p1ptr, *p2ptr;
  1341. int possible[NFEATURES], nposs;
  1342. int ret;
  1343. /* Set scan region to entire image. */
  1344. sx = 0;
  1345. ex = iw;
  1346. sy = 0;
  1347. ey = ih;
  1348. /* Start at first row in region. */
  1349. cy = sy;
  1350. /* While second scan row not outside the bottom of the scan region... */
  1351. while(cy+1 < ey){
  1352. /* Start at beginning of new scan row in region. */
  1353. cx = sx;
  1354. /* While not at end of region's current scan row. */
  1355. while(cx < ex){
  1356. /* Get pixel pair from current x position in current and next */
  1357. /* scan rows. */
  1358. p1ptr = bdata+(cy*iw)+cx;
  1359. p2ptr = bdata+((cy+1)*iw)+cx;
  1360. /* If scan pixel pair matches first pixel pair of */
  1361. /* 1 or more features... */
  1362. if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1363. /* Bump forward to next scan pixel pair. */
  1364. cx++;
  1365. p1ptr++;
  1366. p2ptr++;
  1367. /* If not at end of region's current scan row... */
  1368. if(cx < ex){
  1369. /* If scan pixel pair matches second pixel pair of */
  1370. /* 1 or more features... */
  1371. if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1372. /* Store current x location. */
  1373. x2 = cx;
  1374. /* Skip repeated pixel pairs. */
  1375. skip_repeated_horizontal_pair(&cx, ex, &p1ptr, &p2ptr,
  1376. iw, ih);
  1377. /* If not at end of region's current scan row... */
  1378. if(cx < ex){
  1379. /* If scan pixel pair matches third pixel pair of */
  1380. /* a single feature... */
  1381. if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1382. /* Process detected minutia point. */
  1383. if((ret = process_horizontal_scan_minutia_V2(minutiae,
  1384. cx, cy, x2, possible[0],
  1385. bdata, iw, ih, pdirection_map,
  1386. plow_flow_map, phigh_curve_map,
  1387. lfsparms))){
  1388. /* Return code may be: */
  1389. /* 1. ret< 0 (implying system error) */
  1390. /* 2. ret==IGNORE (ignore current feature) */
  1391. if(ret < 0)
  1392. return(ret);
  1393. /* Otherwise, IGNORE and continue. */
  1394. }
  1395. }
  1396. /* Set up to resume scan. */
  1397. /* Test to see if 3rd pair can slide into 2nd pair. */
  1398. /* The values of the 2nd pair MUST be different. */
  1399. /* If 3rd pair values are different ... */
  1400. if(*p1ptr != *p2ptr){
  1401. /* Set next first pair to last of repeated */
  1402. /* 2nd pairs, ie. back up one pair. */
  1403. cx--;
  1404. }
  1405. /* Otherwise, 3rd pair can't be a 2nd pair, so */
  1406. /* keep pointing to 3rd pair so that it is used */
  1407. /* in the next first pair test. */
  1408. } /* Else, at end of current scan row. */
  1409. }
  1410. /* Otherwise, 2nd pair failed, so keep pointing to it */
  1411. /* so that it is used in the next first pair test. */
  1412. } /* Else, at end of current scan row. */
  1413. }
  1414. /* Otherwise, 1st pair failed... */
  1415. else{
  1416. /* Bump forward to next pixel pair. */
  1417. cx++;
  1418. }
  1419. } /* While not at end of current scan row. */
  1420. /* Bump forward to next scan row. */
  1421. cy++;
  1422. } /* While not out of scan rows. */
  1423. /* Return normally. */
  1424. return(0);
  1425. }
  1426. /*************************************************************************
  1427. **************************************************************************
  1428. #cat: scan4minutiae_vertically - Scans a specified region of binary image data
  1429. #cat: vertically, detecting potential minutiae points.
  1430. #cat: Minutia detected via the vetical scan process are
  1431. #cat: by nature horizontally oriented (orthogonal to the scan).
  1432. #cat: The region actually scanned is slightly larger than that
  1433. #cat: specified. This overlap attempts to minimize the number
  1434. #cat: of minutiae missed at the region boundaries.
  1435. #cat: HOWEVER, some minutiae will still be missed!
  1436. Input:
  1437. bdata - binary image data (0==while & 1==black)
  1438. iw - width (in pixels) of image
  1439. ih - height (in pixels) of image
  1440. imapval - IMAP value associated with this image region
  1441. nmapval - NMAP value associated with this image region
  1442. scan_x - x-pixel coord of origin of region to be scanned
  1443. scan_y - y-pixel coord of origin of region to be scanned
  1444. scan_w - width (in pixels) of region to be scanned
  1445. scan_h - height (in pixels) of region to be scanned
  1446. lfsparms - parameters and thresholds for controlling LFS
  1447. Output:
  1448. minutiae - points to a list of detected minutia structures
  1449. Return Code:
  1450. Zero - successful completion
  1451. Negative - system error
  1452. **************************************************************************/
  1453. int scan4minutiae_vertically(MINUTIAE *minutiae,
  1454. unsigned char *bdata, const int iw, const int ih,
  1455. const int imapval, const int nmapval,
  1456. const int scan_x, const int scan_y,
  1457. const int scan_w, const int scan_h,
  1458. const LFSPARMS *lfsparms)
  1459. {
  1460. int sx, sy, ex, ey, cx, cy, y2;
  1461. unsigned char *p1ptr, *p2ptr;
  1462. int possible[NFEATURES], nposs;
  1463. int ret;
  1464. /* NOTE!!! Minutia that "straddle" region boundaries may be missed! */
  1465. /* If possible, overlap scan region to the right by 1 pixel column. */
  1466. sx = scan_x;
  1467. ex = min(iw, scan_x+scan_w+1);
  1468. /* If possible, overlap top and bottom of current scan region */
  1469. /* by 2 pixel rows to help catch some minutia that straddle the */
  1470. /* the scan region boundaries. */
  1471. sy = max(0, scan_y-2);
  1472. ey = min(ih, scan_y+scan_h+2);
  1473. /* For now, we will not adjust for IMAP edge, as the binary image */
  1474. /* was properly padded at its edges so as not to cause anomalies. */
  1475. /* Start at first column in region. */
  1476. cx = sx;
  1477. /* While second scan column not outside the right of the region ... */
  1478. while(cx+1 < ex){
  1479. /* Start at beginning of new scan column in region. */
  1480. cy = sy;
  1481. /* While not at end of region's current scan column. */
  1482. while(cy < ey){
  1483. /* Get pixel pair from current y position in current and next */
  1484. /* scan columns. */
  1485. p1ptr = bdata+(cy*iw)+cx;
  1486. p2ptr = p1ptr+1;
  1487. /* If scan pixel pair matches first pixel pair of */
  1488. /* 1 or more features... */
  1489. if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1490. /* Bump forward to next scan pixel pair. */
  1491. cy++;
  1492. p1ptr+=iw;
  1493. p2ptr+=iw;
  1494. /* If not at end of region's current scan column... */
  1495. if(cy < ey){
  1496. /* If scan pixel pair matches second pixel pair of */
  1497. /* 1 or more features... */
  1498. if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1499. /* Store current y location. */
  1500. y2 = cy;
  1501. /* Skip repeated pixel pairs. */
  1502. skip_repeated_vertical_pair(&cy, ey, &p1ptr, &p2ptr,
  1503. iw, ih);
  1504. /* If not at end of region's current scan column... */
  1505. if(cy < ey){
  1506. /* If scan pixel pair matches third pixel pair of */
  1507. /* a single feature... */
  1508. if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1509. /* Process detected minutia point. */
  1510. if((ret = process_vertical_scan_minutia(minutiae,
  1511. cx, cy, y2, possible[0],
  1512. bdata, iw, ih,
  1513. imapval, nmapval, lfsparms))){
  1514. /* Return code may be: */
  1515. /* 1. ret< 0 (implying system error) */
  1516. /* 2. ret==IGNORE (ignore current feature) */
  1517. if(ret < 0)
  1518. return(ret);
  1519. /* Otherwise, IGNORE and continue. */
  1520. }
  1521. }
  1522. /* Set up to resume scan. */
  1523. /* Test to see if 3rd pair can slide into 2nd pair. */
  1524. /* The values of the 2nd pair MUST be different. */
  1525. /* If 3rd pair values are different ... */
  1526. if(*p1ptr != *p2ptr){
  1527. /* Set next first pair to last of repeated */
  1528. /* 2nd pairs, ie. back up one pair. */
  1529. cy--;
  1530. }
  1531. /* Otherwise, 3rd pair can't be a 2nd pair, so */
  1532. /* keep pointing to 3rd pair so that it is used */
  1533. /* in the next first pair test. */
  1534. } /* Else, at end of current scan row. */
  1535. }
  1536. /* Otherwise, 2nd pair failed, so keep pointing to it */
  1537. /* so that it is used in the next first pair test. */
  1538. } /* Else, at end of current scan column. */
  1539. }
  1540. /* Otherwise, 1st pair failed... */
  1541. else{
  1542. /* Bump forward to next pixel pair. */
  1543. cy++;
  1544. }
  1545. } /* While not at end of current scan column. */
  1546. /* Bump forward to next scan column. */
  1547. cx++;
  1548. } /* While not out of scan columns. */
  1549. /* Return normally. */
  1550. return(0);
  1551. }
  1552. /*************************************************************************
  1553. **************************************************************************
  1554. #cat: scan4minutiae_vertically_V2 - Scans an entire binary image
  1555. #cat: vertically, detecting potential minutiae points.
  1556. #cat: Minutia detected via the vetical scan process are
  1557. #cat: by nature horizontally oriented (orthogonal to the scan).
  1558. Input:
  1559. bdata - binary image data (0==while & 1==black)
  1560. iw - width (in pixels) of image
  1561. ih - height (in pixels) of image
  1562. pdirection_map - pixelized Direction Map
  1563. plow_flow_map - pixelized Low Ridge Flow Map
  1564. phigh_curve_map - pixelized High Curvature Map
  1565. lfsparms - parameters and thresholds for controlling LFS
  1566. Output:
  1567. minutiae - points to a list of detected minutia structures
  1568. Return Code:
  1569. Zero - successful completion
  1570. Negative - system error
  1571. **************************************************************************/
  1572. int scan4minutiae_vertically_V2(MINUTIAE *minutiae,
  1573. unsigned char *bdata, const int iw, const int ih,
  1574. int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
  1575. const LFSPARMS *lfsparms)
  1576. {
  1577. int sx, sy, ex, ey, cx, cy, y2;
  1578. unsigned char *p1ptr, *p2ptr;
  1579. int possible[NFEATURES], nposs;
  1580. int ret;
  1581. /* Set scan region to entire image. */
  1582. sx = 0;
  1583. ex = iw;
  1584. sy = 0;
  1585. ey = ih;
  1586. /* Start at first column in region. */
  1587. cx = sx;
  1588. /* While second scan column not outside the right of the region ... */
  1589. while(cx+1 < ex){
  1590. /* Start at beginning of new scan column in region. */
  1591. cy = sy;
  1592. /* While not at end of region's current scan column. */
  1593. while(cy < ey){
  1594. /* Get pixel pair from current y position in current and next */
  1595. /* scan columns. */
  1596. p1ptr = bdata+(cy*iw)+cx;
  1597. p2ptr = p1ptr+1;
  1598. /* If scan pixel pair matches first pixel pair of */
  1599. /* 1 or more features... */
  1600. if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1601. /* Bump forward to next scan pixel pair. */
  1602. cy++;
  1603. p1ptr+=iw;
  1604. p2ptr+=iw;
  1605. /* If not at end of region's current scan column... */
  1606. if(cy < ey){
  1607. /* If scan pixel pair matches second pixel pair of */
  1608. /* 1 or more features... */
  1609. if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1610. /* Store current y location. */
  1611. y2 = cy;
  1612. /* Skip repeated pixel pairs. */
  1613. skip_repeated_vertical_pair(&cy, ey, &p1ptr, &p2ptr,
  1614. iw, ih);
  1615. /* If not at end of region's current scan column... */
  1616. if(cy < ey){
  1617. /* If scan pixel pair matches third pixel pair of */
  1618. /* a single feature... */
  1619. if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
  1620. /* Process detected minutia point. */
  1621. if((ret = process_vertical_scan_minutia_V2(minutiae,
  1622. cx, cy, y2, possible[0],
  1623. bdata, iw, ih, pdirection_map,
  1624. plow_flow_map, phigh_curve_map,
  1625. lfsparms))){
  1626. /* Return code may be: */
  1627. /* 1. ret< 0 (implying system error) */
  1628. /* 2. ret==IGNORE (ignore current feature) */
  1629. if(ret < 0)
  1630. return(ret);
  1631. /* Otherwise, IGNORE and continue. */
  1632. }
  1633. }
  1634. /* Set up to resume scan. */
  1635. /* Test to see if 3rd pair can slide into 2nd pair. */
  1636. /* The values of the 2nd pair MUST be different. */
  1637. /* If 3rd pair values are different ... */
  1638. if(*p1ptr != *p2ptr){
  1639. /* Set next first pair to last of repeated */
  1640. /* 2nd pairs, ie. back up one pair. */
  1641. cy--;
  1642. }
  1643. /* Otherwise, 3rd pair can't be a 2nd pair, so */
  1644. /* keep pointing to 3rd pair so that it is used */
  1645. /* in the next first pair test. */
  1646. } /* Else, at end of current scan row. */
  1647. }
  1648. /* Otherwise, 2nd pair failed, so keep pointing to it */
  1649. /* so that it is used in the next first pair test. */
  1650. } /* Else, at end of current scan column. */
  1651. }
  1652. /* Otherwise, 1st pair failed... */
  1653. else{
  1654. /* Bump forward to next pixel pair. */
  1655. cy++;
  1656. }
  1657. } /* While not at end of current scan column. */
  1658. /* Bump forward to next scan column. */
  1659. cx++;
  1660. } /* While not out of scan columns. */
  1661. /* Return normally. */
  1662. return(0);
  1663. }
  1664. /*************************************************************************
  1665. **************************************************************************
  1666. #cat: rescan4minutiae_horizontally - Rescans portions of a block of binary
  1667. #cat: image data horizontally for potential minutiae. The areas
  1668. #cat: rescanned within the block are based on the current
  1669. #cat: block's neighboring blocks' IMAP and NMAP values.
  1670. Input:
  1671. bdata - binary image data (0==while & 1==black)
  1672. iw - width (in pixels) of image
  1673. ih - height (in pixels) of image
  1674. imap - matrix of ridge flow directions
  1675. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  1676. blocks which have no neighboring valid directions.
  1677. blk_x - x-block coord to be rescanned
  1678. blk_y - y-block coord to be rescanned
  1679. mw - width (in blocks) of IMAP and NMAP matrices.
  1680. mh - height (in blocks) of IMAP and NMAP matrices.
  1681. scan_x - x-pixel coord of origin of region to be rescanned
  1682. scan_y - y-pixel coord of origin of region to be rescanned
  1683. scan_w - width (in pixels) of region to be rescanned
  1684. scan_h - height (in pixels) of region to be rescanned
  1685. lfsparms - parameters and thresholds for controlling LFS
  1686. Output:
  1687. minutiae - points to a list of detected minutia structures
  1688. Return Code:
  1689. Zero - successful completion
  1690. Negative - system error
  1691. **************************************************************************/
  1692. int rescan4minutiae_horizontally(MINUTIAE *minutiae,
  1693. unsigned char *bdata, const int iw, const int ih,
  1694. const int *imap, const int *nmap,
  1695. const int blk_x, const int blk_y,
  1696. const int mw, const int mh,
  1697. const int scan_x, const int scan_y,
  1698. const int scan_w, const int scan_h,
  1699. const LFSPARMS *lfsparms)
  1700. {
  1701. int blk_i, ret;
  1702. /* Compute block index from block coordinates. */
  1703. blk_i = (blk_y*mw)+blk_x;
  1704. /* If high-curve block... */
  1705. if(nmap[blk_i] == HIGH_CURVATURE){
  1706. /* Rescan entire block in orthogonal direction. */
  1707. if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
  1708. imap[blk_i], nmap[blk_i],
  1709. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1710. /* Return code may be: */
  1711. /* 1. ret<0 (implying system error) */
  1712. return(ret);
  1713. }
  1714. /* Otherwise, block is low-curvature. */
  1715. else{
  1716. /* 1. Rescan horizontally to the North. */
  1717. if((ret = rescan_partial_horizontally(NORTH, minutiae, bdata, iw, ih,
  1718. imap, nmap, blk_x, blk_y, mw, mh,
  1719. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1720. /* Return code may be: */
  1721. /* 1. ret<0 (implying system error) */
  1722. return(ret);
  1723. /* 2. Rescan horizontally to the East. */
  1724. if((ret = rescan_partial_horizontally(EAST, minutiae, bdata, iw, ih,
  1725. imap, nmap, blk_x, blk_y, mw, mh,
  1726. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1727. return(ret);
  1728. /* 3. Rescan horizontally to the South. */
  1729. if((ret = rescan_partial_horizontally(SOUTH, minutiae, bdata, iw, ih,
  1730. imap, nmap, blk_x, blk_y, mw, mh,
  1731. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1732. return(ret);
  1733. /* 4. Rescan horizontally to the West. */
  1734. if((ret = rescan_partial_horizontally(WEST, minutiae, bdata, iw, ih,
  1735. imap, nmap, blk_x, blk_y, mw, mh,
  1736. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1737. return(ret);
  1738. } /* End low-curvature rescan. */
  1739. /* Return normally. */
  1740. return(0);
  1741. }
  1742. /*************************************************************************
  1743. **************************************************************************
  1744. #cat: rescan4minutiae_vertically - Rescans portions of a block of binary
  1745. #cat: image data vertically for potential minutiae. The areas
  1746. #cat: rescanned within the block are based on the current
  1747. #cat: block's neighboring blocks' IMAP and NMAP values.
  1748. Input:
  1749. bdata - binary image data (0==while & 1==black)
  1750. iw - width (in pixels) of image
  1751. ih - height (in pixels) of image
  1752. imap - matrix of ridge flow directions
  1753. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  1754. blocks which have no neighboring valid directions.
  1755. blk_x - x-block coord to be rescanned
  1756. blk_y - y-block coord to be rescanned
  1757. mw - width (in blocks) of IMAP and NMAP matrices.
  1758. mh - height (in blocks) of IMAP and NMAP matrices.
  1759. scan_x - x-pixel coord of origin of region to be rescanned
  1760. scan_y - y-pixel coord of origin of region to be rescanned
  1761. scan_w - width (in pixels) of region to be rescanned
  1762. scan_h - height (in pixels) of region to be rescanned
  1763. lfsparms - parameters and thresholds for controlling LFS
  1764. Output:
  1765. minutiae - points to a list of detected minutia structures
  1766. Return Code:
  1767. Zero - successful completion
  1768. Negative - system error
  1769. **************************************************************************/
  1770. int rescan4minutiae_vertically(MINUTIAE *minutiae,
  1771. unsigned char *bdata, const int iw, const int ih,
  1772. const int *imap, const int *nmap,
  1773. const int blk_x, const int blk_y,
  1774. const int mw, const int mh,
  1775. const int scan_x, const int scan_y,
  1776. const int scan_w, const int scan_h,
  1777. const LFSPARMS *lfsparms)
  1778. {
  1779. int blk_i, ret;
  1780. /* Compute block index from block coordinates. */
  1781. blk_i = (blk_y*mw)+blk_x;
  1782. /* If high-curve block... */
  1783. if(nmap[blk_i] == HIGH_CURVATURE){
  1784. /* Rescan entire block in orthogonal direction. */
  1785. if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
  1786. imap[blk_i], nmap[blk_i],
  1787. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1788. /* Return code may be: */
  1789. /* 1. ret<0 (implying system error) */
  1790. return(ret);
  1791. }
  1792. /* Otherwise, block is low-curvature. */
  1793. else{
  1794. /* 1. Rescan vertically to the North. */
  1795. if((ret = rescan_partial_vertically(NORTH, minutiae, bdata, iw, ih,
  1796. imap, nmap, blk_x, blk_y, mw, mh,
  1797. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1798. /* Return code may be: */
  1799. /* 1. ret<0 (implying system error) */
  1800. return(ret);
  1801. /* 2. Rescan vertically to the East. */
  1802. if((ret = rescan_partial_vertically(EAST, minutiae, bdata, iw, ih,
  1803. imap, nmap, blk_x, blk_y, mw, mh,
  1804. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1805. return(ret);
  1806. /* 3. Rescan vertically to the South. */
  1807. if((ret = rescan_partial_vertically(SOUTH, minutiae, bdata, iw, ih,
  1808. imap, nmap, blk_x, blk_y, mw, mh,
  1809. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1810. return(ret);
  1811. /* 4. Rescan vertically to the West. */
  1812. if((ret = rescan_partial_vertically(WEST, minutiae, bdata, iw, ih,
  1813. imap, nmap, blk_x, blk_y, mw, mh,
  1814. scan_x, scan_y, scan_w, scan_h, lfsparms)))
  1815. return(ret);
  1816. } /* End low-curvature rescan. */
  1817. /* Return normally. */
  1818. return(0);
  1819. }
  1820. /*************************************************************************
  1821. **************************************************************************
  1822. #cat: rescan_partial_horizontally - Rescans a portion of a block of binary
  1823. #cat: image data horizontally based on the IMAP and NMAP values
  1824. #cat: of a specified neighboring block.
  1825. Input:
  1826. nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
  1827. bdata - binary image data (0==while & 1==black)
  1828. iw - width (in pixels) of image
  1829. ih - height (in pixels) of image
  1830. imap - matrix of ridge flow directions
  1831. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  1832. blocks which have no neighboring valid directions.
  1833. blk_x - x-block coord to be rescanned
  1834. blk_y - y-block coord to be rescanned
  1835. mw - width (in blocks) of IMAP and NMAP matrices.
  1836. mh - height (in blocks) of IMAP and NMAP matrices.
  1837. scan_x - x-pixel coord of origin of image region
  1838. scan_y - y-pixel coord of origin of image region
  1839. scan_w - width (in pixels) of image region
  1840. scan_h - height (in pixels) of image region
  1841. lfsparms - parameters and thresholds for controlling LFS
  1842. Output:
  1843. minutiae - points to a list of detected minutia structures
  1844. Return Code:
  1845. Zero - successful completion
  1846. Negative - system error
  1847. **************************************************************************/
  1848. int rescan_partial_horizontally(const int nbr_dir, MINUTIAE *minutiae,
  1849. unsigned char *bdata, const int iw, const int ih,
  1850. const int *imap, const int *nmap,
  1851. const int blk_x, const int blk_y,
  1852. const int mw, const int mh,
  1853. const int scan_x, const int scan_y,
  1854. const int scan_w, const int scan_h,
  1855. const LFSPARMS *lfsparms)
  1856. {
  1857. int nblk_i, blk_i;
  1858. int rescan_dir;
  1859. int rescan_x, rescan_y, rescan_w, rescan_h;
  1860. int ret;
  1861. /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
  1862. ret = get_nbr_block_index(&nblk_i, nbr_dir, blk_x, blk_y, mw, mh);
  1863. /* Will return: */
  1864. /* 1. Neighbor index found == FOUND */
  1865. /* 2. Neighbor not found == NOT_FOUND */
  1866. /* 3. System error < 0 */
  1867. /* If system error ... */
  1868. if(ret < 0)
  1869. /* Return the error code. */
  1870. return(ret);
  1871. /* If neighbor not found ... */
  1872. if(ret == NOT_FOUND)
  1873. /* Nothing to do, so return normally. */
  1874. return(0);
  1875. /* Otherwise, neighboring block found ... */
  1876. /* If neighbor block is VALID... */
  1877. if(imap[nblk_i] != INVALID_DIR){
  1878. /* Compute block index from current (not neighbor) block coordinates. */
  1879. blk_i = (blk_y*mw)+blk_x;
  1880. /* Select feature scan direction based on neighbor IMAP. */
  1881. rescan_dir = choose_scan_direction(imap[nblk_i],
  1882. lfsparms->num_directions);
  1883. /* If new scan direction is HORIZONTAL... */
  1884. if(rescan_dir == SCAN_HORIZONTAL){
  1885. /* Adjust scan_x, scan_y, scan_w, scan_h for rescan. */
  1886. if((ret = adjust_horizontal_rescan(nbr_dir, &rescan_x, &rescan_y,
  1887. &rescan_w, &rescan_h,
  1888. scan_x, scan_y, scan_w, scan_h, lfsparms->blocksize)))
  1889. /* Return system error code. */
  1890. return(ret);
  1891. /* Rescan specified region in block vertically. */
  1892. /* Pass IMAP direction for the block, NOT its neighbor. */
  1893. if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
  1894. imap[blk_i], nmap[blk_i],
  1895. rescan_x, rescan_y, rescan_w, rescan_h, lfsparms)))
  1896. /* Return code may be: */
  1897. /* 1. ret<0 (implying system error) */
  1898. return(ret);
  1899. } /* Otherwise, block has already been scanned vertically. */
  1900. } /* Otherwise, neighbor has INVALID IMAP, so ignore rescan. */
  1901. /* Return normally. */
  1902. return(0);
  1903. }
  1904. /*************************************************************************
  1905. **************************************************************************
  1906. #cat: rescan_partial_vertically - Rescans a portion of a block of binary
  1907. #cat: image data vertically based on the IMAP and NMAP values
  1908. #cat: of a specified neighboring block.
  1909. Input:
  1910. nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
  1911. bdata - binary image data (0==while & 1==black)
  1912. iw - width (in pixels) of image
  1913. ih - height (in pixels) of image
  1914. imap - matrix of ridge flow directions
  1915. nmap - IMAP augmented with blocks of HIGH-CURVATURE and
  1916. blocks which have no neighboring valid directions.
  1917. blk_x - x-block coord to be rescanned
  1918. blk_y - y-block coord to be rescanned
  1919. mw - width (in blocks) of IMAP and NMAP matrices.
  1920. mh - height (in blocks) of IMAP and NMAP matrices.
  1921. scan_x - x-pixel coord of origin of image region
  1922. scan_y - y-pixel coord of origin of image region
  1923. scan_w - width (in pixels) of image region
  1924. scan_h - height (in pixels) of image region
  1925. lfsparms - parameters and thresholds for controlling LFS
  1926. Output:
  1927. minutiae - points to a list of detected minutia structures
  1928. Return Code:
  1929. Zero - successful completion
  1930. Negative - system error
  1931. **************************************************************************/
  1932. int rescan_partial_vertically(const int nbr_dir, MINUTIAE *minutiae,
  1933. unsigned char *bdata, const int iw, const int ih,
  1934. const int *imap, const int *nmap,
  1935. const int blk_x, const int blk_y,
  1936. const int mw, const int mh,
  1937. const int scan_x, const int scan_y,
  1938. const int scan_w, const int scan_h,
  1939. const LFSPARMS *lfsparms)
  1940. {
  1941. int nblk_i, blk_i;
  1942. int rescan_dir;
  1943. int rescan_x, rescan_y, rescan_w, rescan_h;
  1944. int ret;
  1945. /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
  1946. ret = get_nbr_block_index(&nblk_i, nbr_dir, blk_x, blk_y, mw, mh);
  1947. /* Will return: */
  1948. /* 1. Neighbor index found == FOUND */
  1949. /* 2. Neighbor not found == NOT_FOUND */
  1950. /* 3. System error < 0 */
  1951. /* If system error ... */
  1952. if(ret < 0)
  1953. /* Return the error code. */
  1954. return(ret);
  1955. /* If neighbor not found ... */
  1956. if(ret == NOT_FOUND)
  1957. /* Nothing to do, so return normally. */
  1958. return(0);
  1959. /* Otherwise, neighboring block found ... */
  1960. /* If neighbor block is VALID... */
  1961. if(imap[nblk_i] != INVALID_DIR){
  1962. /* Compute block index from current (not neighbor) block coordinates. */
  1963. blk_i = (blk_y*mw)+blk_x;
  1964. /* Select feature scan direction based on neighbor IMAP. */
  1965. rescan_dir = choose_scan_direction(imap[nblk_i],
  1966. lfsparms->num_directions);
  1967. /* If new scan direction is VERTICAL... */
  1968. if(rescan_dir == SCAN_VERTICAL){
  1969. /* Adjust scan_x, scan_y, scan_w, scan_h for rescan. */
  1970. if((ret = adjust_vertical_rescan(nbr_dir, &rescan_x, &rescan_y,
  1971. &rescan_w, &rescan_h,
  1972. scan_x, scan_y, scan_w, scan_h, lfsparms->blocksize)))
  1973. /* Return system error code. */
  1974. return(ret);
  1975. /* Rescan specified region in block vertically. */
  1976. /* Pass IMAP direction for the block, NOT its neighbor. */
  1977. if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
  1978. imap[blk_i], nmap[blk_i],
  1979. rescan_x, rescan_y, rescan_w, rescan_h, lfsparms)))
  1980. /* Return code may be: */
  1981. /* 1. ret<0 (implying system error) */
  1982. return(ret);
  1983. } /* Otherwise, block has already been scanned horizontally. */
  1984. } /* Otherwise, neighbor has INVALID IMAP, so ignore rescan. */
  1985. /* Return normally. */
  1986. return(0);
  1987. }
  1988. /*************************************************************************
  1989. **************************************************************************
  1990. #cat: get_nbr_block_index - Determines the block index (if one exists)
  1991. #cat: for a specified neighbor of a block in the image.
  1992. Input:
  1993. nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
  1994. blk_x - x-block coord to find neighbor of
  1995. blk_y - y-block coord to find neighbor of
  1996. mw - width (in blocks) of IMAP and NMAP matrices.
  1997. mh - height (in blocks) of IMAP and NMAP matrices.
  1998. Output:
  1999. oblk_i - points to neighbor's block index
  2000. Return Code:
  2001. NOT_FOUND - neighbor index does not exist
  2002. FOUND - neighbor index exists and returned
  2003. Negative - system error
  2004. **************************************************************************/
  2005. int get_nbr_block_index(int *oblk_i, const int nbr_dir,
  2006. const int blk_x, const int blk_y, const int mw, const int mh)
  2007. {
  2008. int nx, ny, ni;
  2009. switch(nbr_dir){
  2010. case NORTH:
  2011. /* If neighbor doesn't exist above... */
  2012. if((ny = blk_y-1) < 0)
  2013. /* Done, so return normally. */
  2014. return(NOT_FOUND);
  2015. /* Get neighbor's block index. */
  2016. ni = (ny*mw)+blk_x;
  2017. break;
  2018. case EAST:
  2019. /* If neighbor doesn't exist to the right... */
  2020. if((nx = blk_x+1) >= mw)
  2021. /* Done, so return normally. */
  2022. return(NOT_FOUND);
  2023. /* Get neighbor's block index. */
  2024. ni = (blk_y*mw)+nx;
  2025. break;
  2026. case SOUTH:
  2027. /* If neighbor doesn't exist below... */
  2028. if((ny = blk_y+1) >= mh)
  2029. /* Return normally. */
  2030. return(NOT_FOUND);
  2031. /* Get neighbor's block index. */
  2032. ni = (ny*mw)+blk_x;
  2033. break;
  2034. case WEST:
  2035. /* If neighbor doesn't exist to the left... */
  2036. if((nx = blk_x-1) < 0)
  2037. /* Return normally. */
  2038. return(NOT_FOUND);
  2039. /* Get neighbor's block index. */
  2040. ni = (blk_y*mw)+nx;
  2041. break;
  2042. default:
  2043. fprintf(stderr,
  2044. "ERROR : get_nbr_block_index : illegal neighbor direction\n");
  2045. return(-200);
  2046. }
  2047. /* Assign output pointer. */
  2048. *oblk_i = ni;
  2049. /* Return neighbor FOUND. */
  2050. return(FOUND);
  2051. }
  2052. /*************************************************************************
  2053. **************************************************************************
  2054. #cat: adjust_horizontal_rescan - Determines the portion of an image block to
  2055. #cat: be rescanned horizontally based on a specified neighbor.
  2056. Input:
  2057. nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
  2058. scan_x - x-pixel coord of origin of image region
  2059. scan_y - y-pixel coord of origin of image region
  2060. scan_w - width (in pixels) of image region
  2061. scan_h - height (in pixels) of image region
  2062. blocksize - dimension of image blocks (in pixels)
  2063. Output:
  2064. rescan_x - x-pixel coord of origin of region to be rescanned
  2065. rescan_y - y-pixel coord of origin of region to be rescanned
  2066. rescan_w - width (in pixels) of region to be rescanned
  2067. rescan_h - height (in pixels) of region to be rescanned
  2068. Return Code:
  2069. Zero - successful completion
  2070. Negative - system error
  2071. **************************************************************************/
  2072. int adjust_horizontal_rescan(const int nbr_dir, int *rescan_x, int *rescan_y,
  2073. int *rescan_w, int *rescan_h, const int scan_x, const int scan_y,
  2074. const int scan_w, const int scan_h, const int blocksize)
  2075. {
  2076. int half_blocksize, qtr_blocksize;
  2077. /* Compute half of blocksize. */
  2078. half_blocksize = blocksize>>1;
  2079. /* Compute quarter of blocksize. */
  2080. qtr_blocksize = blocksize>>2;
  2081. /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
  2082. switch(nbr_dir){
  2083. case NORTH:
  2084. /*
  2085. *************************
  2086. * RESCAN NORTH *
  2087. * AREA *
  2088. *************************
  2089. | |
  2090. | |
  2091. | |
  2092. | |
  2093. | |
  2094. | |
  2095. -------------------------
  2096. */
  2097. /* Rescan origin stays the same. */
  2098. *rescan_x = scan_x;
  2099. *rescan_y = scan_y;
  2100. /* Rescan width stays the same. */
  2101. *rescan_w = scan_w;
  2102. /* Rescan height is reduced to "qtr_blocksize" */
  2103. /* if scan_h is larger. */
  2104. *rescan_h = min(qtr_blocksize, scan_h);
  2105. break;
  2106. case EAST:
  2107. /*
  2108. ------------*************
  2109. | * *
  2110. | * *
  2111. | * E R *
  2112. | * A E *
  2113. | * S S *
  2114. | * T C *
  2115. | * A *
  2116. | * N *
  2117. | * *
  2118. | * *
  2119. ------------*************
  2120. */
  2121. /* Rescan x-orign is set to half_blocksize from right edge of */
  2122. /* block if scan width is larger. */
  2123. *rescan_x = max(scan_x+scan_w-half_blocksize, scan_x);
  2124. /* Rescan y-origin stays the same. */
  2125. *rescan_y = scan_y;
  2126. /* Rescan width is reduced to "half_blocksize" */
  2127. /* if scan width is larger. */
  2128. *rescan_w = min(half_blocksize, scan_w);
  2129. /* Rescan height stays the same. */
  2130. *rescan_h = scan_h;
  2131. break;
  2132. case SOUTH:
  2133. /*
  2134. -------------------------
  2135. | |
  2136. | |
  2137. | |
  2138. | |
  2139. | |
  2140. | |
  2141. *************************
  2142. * RESCAN SOUTH *
  2143. * AREA *
  2144. *************************
  2145. */
  2146. /* Rescan x-origin stays the same. */
  2147. *rescan_x = scan_x;
  2148. /* Rescan y-orign is set to qtr_blocksize from bottom edge of */
  2149. /* block if scan height is larger. */
  2150. *rescan_y = max(scan_y+scan_h-qtr_blocksize, scan_y);
  2151. /* Rescan width stays the same. */
  2152. *rescan_w = scan_w;
  2153. /* Rescan height is reduced to "qtr_blocksize" */
  2154. /* if scan height is larger. */
  2155. *rescan_h = min(qtr_blocksize, scan_h);
  2156. break;
  2157. case WEST:
  2158. /*
  2159. *************------------
  2160. * * |
  2161. * * |
  2162. * W R * |
  2163. * E E * |
  2164. * S S * |
  2165. * T C * |
  2166. * A * |
  2167. * N * |
  2168. * * |
  2169. * * |
  2170. *************------------
  2171. */
  2172. /* Rescan origin stays the same. */
  2173. *rescan_x = scan_x;
  2174. *rescan_y = scan_y;
  2175. /* Rescan width is reduced to "half_blocksize" */
  2176. /* if scan width is larger. */
  2177. *rescan_w = min(half_blocksize, scan_w);
  2178. /* Rescan height stays the same. */
  2179. *rescan_h = scan_h;
  2180. break;
  2181. default:
  2182. fprintf(stderr,
  2183. "ERROR : adjust_horizontal_rescan : illegal neighbor direction\n");
  2184. return(-210);
  2185. }
  2186. /* Return normally. */
  2187. return(0);
  2188. }
  2189. /*************************************************************************
  2190. **************************************************************************
  2191. #cat: adjust_vertical_rescan - Determines the portion of an image block to
  2192. #cat: be rescanned vertically based on a specified neighbor.
  2193. Input:
  2194. nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
  2195. scan_x - x-pixel coord of origin of image region
  2196. scan_y - y-pixel coord of origin of image region
  2197. scan_w - width (in pixels) of image region
  2198. scan_h - height (in pixels) of image region
  2199. blocksize - dimension of image blocks (in pixels)
  2200. Output:
  2201. rescan_x - x-pixel coord of origin of region to be rescanned
  2202. rescan_y - y-pixel coord of origin of region to be rescanned
  2203. rescan_w - width (in pixels) of region to be rescanned
  2204. rescan_h - height (in pixels) of region to be rescanned
  2205. Return Code:
  2206. Zero - successful completion
  2207. Negative - system error
  2208. **************************************************************************/
  2209. int adjust_vertical_rescan(const int nbr_dir, int *rescan_x, int *rescan_y,
  2210. int *rescan_w, int *rescan_h, const int scan_x, const int scan_y,
  2211. const int scan_w, const int scan_h, const int blocksize)
  2212. {
  2213. int half_blocksize, qtr_blocksize;
  2214. /* Compute half of blocksize. */
  2215. half_blocksize = blocksize>>1;
  2216. /* Compute quarter of blocksize. */
  2217. qtr_blocksize = blocksize>>2;
  2218. /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
  2219. switch(nbr_dir){
  2220. case NORTH:
  2221. /*
  2222. *************************
  2223. * *
  2224. * RESCAN NORTH *
  2225. * AREA *
  2226. * *
  2227. *************************
  2228. | |
  2229. | |
  2230. | |
  2231. | |
  2232. | |
  2233. -------------------------
  2234. */
  2235. /* Rescan origin stays the same. */
  2236. *rescan_x = scan_x;
  2237. *rescan_y = scan_y;
  2238. /* Rescan width stays the same. */
  2239. *rescan_w = scan_w;
  2240. /* Rescan height is reduced to "half_blocksize" */
  2241. /* if scan_h is larger. */
  2242. *rescan_h = min(half_blocksize, scan_h);
  2243. break;
  2244. case EAST:
  2245. /*
  2246. ------------------*******
  2247. | * *
  2248. | * *
  2249. | * E R *
  2250. | * A E *
  2251. | * S S *
  2252. | * T C *
  2253. | * A *
  2254. | * N *
  2255. | * *
  2256. | * *
  2257. ------------------*******
  2258. */
  2259. /* Rescan x-orign is set to qtr_blocksize from right edge of */
  2260. /* block if scan width is larger. */
  2261. *rescan_x = max(scan_x+scan_w-qtr_blocksize, scan_x);
  2262. /* Rescan y-origin stays the same. */
  2263. *rescan_y = scan_y;
  2264. /* Rescan width is reduced to "qtr_blocksize" */
  2265. /* if scan width is larger. */
  2266. *rescan_w = min(qtr_blocksize, scan_w);
  2267. /* Rescan height stays the same. */
  2268. *rescan_h = scan_h;
  2269. break;
  2270. case SOUTH:
  2271. /*
  2272. -------------------------
  2273. | |
  2274. | |
  2275. | |
  2276. | |
  2277. | |
  2278. *************************
  2279. * *
  2280. * RESCAN SOUTH *
  2281. * AREA *
  2282. * *
  2283. *************************
  2284. */
  2285. /* Rescan x-origin stays the same. */
  2286. *rescan_x = scan_x;
  2287. /* Rescan y-orign is set to half_blocksize from bottom edge of */
  2288. /* block if scan height is larger. */
  2289. *rescan_y = max(scan_y+scan_h-half_blocksize, scan_y);
  2290. /* Rescan width stays the same. */
  2291. *rescan_w = scan_w;
  2292. /* Rescan height is reduced to "half_blocksize" */
  2293. /* if scan height is larger. */
  2294. *rescan_h = min(half_blocksize, scan_h);
  2295. break;
  2296. case WEST:
  2297. /*
  2298. *******------------------
  2299. * * |
  2300. * * |
  2301. * W R * |
  2302. * E E * |
  2303. * S S * |
  2304. * T C * |
  2305. * A * |
  2306. * N * |
  2307. * * |
  2308. * * |
  2309. *******------------------
  2310. */
  2311. /* Rescan origin stays the same. */
  2312. *rescan_x = scan_x;
  2313. *rescan_y = scan_y;
  2314. /* Rescan width is reduced to "qtr_blocksize" */
  2315. /* if scan width is larger. */
  2316. *rescan_w = min(qtr_blocksize, scan_w);
  2317. /* Rescan height stays the same. */
  2318. *rescan_h = scan_h;
  2319. break;
  2320. default:
  2321. fprintf(stderr,
  2322. "ERROR : adjust_vertical_rescan : illegal neighbor direction\n");
  2323. return(-220);
  2324. }
  2325. /* Return normally. */
  2326. return(0);
  2327. }
  2328. /*************************************************************************
  2329. **************************************************************************
  2330. #cat: process_horizontal_scan_minutia - Takes a minutia point that was
  2331. #cat: detected via the horizontal scan process and
  2332. #cat: adjusts its location (if necessary), determines its
  2333. #cat: direction, and (if it is not already in the minutiae
  2334. #cat: list) adds it to the list. These minutia are by nature
  2335. #cat: vertical in orientation (orthogonal to the scan).
  2336. Input:
  2337. cx - x-pixel coord where 3rd pattern pair of mintuia was detected
  2338. cy - y-pixel coord where 3rd pattern pair of mintuia was detected
  2339. y2 - y-pixel coord where 2nd pattern pair of mintuia was detected
  2340. feature_id - type of minutia (ex. index into feature_patterns[] list)
  2341. bdata - binary image data (0==while & 1==black)
  2342. iw - width (in pixels) of image
  2343. ih - height (in pixels) of image
  2344. imapval - IMAP value associated with this image region
  2345. nmapval - NMAP value associated with this image region
  2346. lfsparms - parameters and thresholds for controlling LFS
  2347. Output:
  2348. minutiae - points to a list of detected minutia structures
  2349. Return Code:
  2350. Zero - successful completion
  2351. IGNORE - minutia is to be ignored
  2352. Negative - system error
  2353. **************************************************************************/
  2354. int process_horizontal_scan_minutia(MINUTIAE *minutiae,
  2355. const int cx, const int cy,
  2356. const int x2, const int feature_id,
  2357. unsigned char *bdata, const int iw, const int ih,
  2358. const int imapval, const int nmapval,
  2359. const LFSPARMS *lfsparms)
  2360. {
  2361. MINUTIA *minutia;
  2362. int x_loc, y_loc;
  2363. int x_edge, y_edge;
  2364. int idir, ret;
  2365. /* Set x location of minutia point to be half way between */
  2366. /* first position of second feature pair and position of */
  2367. /* third feature pair. */
  2368. x_loc = (cx + x2)>>1;
  2369. /* Set same x location to neighboring edge pixel. */
  2370. x_edge = x_loc;
  2371. /* Feature location should always point to either ending */
  2372. /* of ridge or (for bifurcations) ending of valley. */
  2373. /* So, if detected feature is APPEARING... */
  2374. if(feature_patterns[feature_id].appearing){
  2375. /* Set y location to second scan row. */
  2376. y_loc = cy+1;
  2377. /* Set y location of neighboring edge pixel to the first scan row. */
  2378. y_edge = cy;
  2379. }
  2380. /* Otherwise, feature is DISAPPEARING... */
  2381. else{
  2382. /* Set y location to first scan row. */
  2383. y_loc = cy;
  2384. /* Set y location of neighboring edge pixel to the second scan row. */
  2385. y_edge = cy+1;
  2386. }
  2387. /* If current minutia is in a high-curvature block... */
  2388. if(nmapval == HIGH_CURVATURE){
  2389. /* Adjust location and direction locally. */
  2390. if((ret = adjust_high_curvature_minutia(&idir, &x_loc, &y_loc,
  2391. &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
  2392. bdata, iw, ih, minutiae, lfsparms))){
  2393. /* Could be a system error or IGNORE minutia. */
  2394. return(ret);
  2395. }
  2396. /* Otherwise, we have our high-curvature minutia attributes. */
  2397. }
  2398. /* Otherwise, minutia is in fairly low-curvature block... */
  2399. else{
  2400. /* Get minutia direction based on current IMAP value. */
  2401. idir = get_low_curvature_direction(SCAN_HORIZONTAL,
  2402. feature_patterns[feature_id].appearing,
  2403. imapval, lfsparms->num_directions);
  2404. }
  2405. /* Create a minutia object based on derived attributes. */
  2406. if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
  2407. DEFAULT_RELIABILITY,
  2408. feature_patterns[feature_id].type,
  2409. feature_patterns[feature_id].appearing, feature_id)))
  2410. /* Return system error. */
  2411. return(ret);
  2412. /* Update the minutiae list with potential new minutia. */
  2413. ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
  2414. /* If minuitia IGNORED and not added to the minutia list ... */
  2415. if(ret == IGNORE)
  2416. /* Deallocate the minutia. */
  2417. free_minutia(minutia);
  2418. /* Otherwise, return normally. */
  2419. return(0);
  2420. }
  2421. /*************************************************************************
  2422. **************************************************************************
  2423. #cat: process_horizontal_scan_minutia_V2 - Takes a minutia point that was
  2424. #cat: detected via the horizontal scan process and
  2425. #cat: adjusts its location (if necessary), determines its
  2426. #cat: direction, and (if it is not already in the minutiae
  2427. #cat: list) adds it to the list. These minutia are by nature
  2428. #cat: vertical in orientation (orthogonal to the scan).
  2429. Input:
  2430. cx - x-pixel coord where 3rd pattern pair of mintuia was detected
  2431. cy - y-pixel coord where 3rd pattern pair of mintuia was detected
  2432. y2 - y-pixel coord where 2nd pattern pair of mintuia was detected
  2433. feature_id - type of minutia (ex. index into feature_patterns[] list)
  2434. bdata - binary image data (0==while & 1==black)
  2435. iw - width (in pixels) of image
  2436. ih - height (in pixels) of image
  2437. pdirection_map - pixelized Direction Map
  2438. plow_flow_map - pixelized Low Ridge Flow Map
  2439. phigh_curve_map - pixelized High Curvature Map
  2440. lfsparms - parameters and thresholds for controlling LFS
  2441. Output:
  2442. minutiae - points to a list of detected minutia structures
  2443. Return Code:
  2444. Zero - successful completion
  2445. IGNORE - minutia is to be ignored
  2446. Negative - system error
  2447. **************************************************************************/
  2448. int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
  2449. const int cx, const int cy,
  2450. const int x2, const int feature_id,
  2451. unsigned char *bdata, const int iw, const int ih,
  2452. int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
  2453. const LFSPARMS *lfsparms)
  2454. {
  2455. MINUTIA *minutia;
  2456. int x_loc, y_loc;
  2457. int x_edge, y_edge;
  2458. int idir, ret;
  2459. int dmapval, fmapval, cmapval;
  2460. double reliability;
  2461. /* Set x location of minutia point to be half way between */
  2462. /* first position of second feature pair and position of */
  2463. /* third feature pair. */
  2464. x_loc = (cx + x2)>>1;
  2465. /* Set same x location to neighboring edge pixel. */
  2466. x_edge = x_loc;
  2467. /* Feature location should always point to either ending */
  2468. /* of ridge or (for bifurcations) ending of valley. */
  2469. /* So, if detected feature is APPEARING... */
  2470. if(feature_patterns[feature_id].appearing){
  2471. /* Set y location to second scan row. */
  2472. y_loc = cy+1;
  2473. /* Set y location of neighboring edge pixel to the first scan row. */
  2474. y_edge = cy;
  2475. }
  2476. /* Otherwise, feature is DISAPPEARING... */
  2477. else{
  2478. /* Set y location to first scan row. */
  2479. y_loc = cy;
  2480. /* Set y location of neighboring edge pixel to the second scan row. */
  2481. y_edge = cy+1;
  2482. }
  2483. dmapval = *(pdirection_map+(y_loc*iw)+x_loc);
  2484. fmapval = *(plow_flow_map+(y_loc*iw)+x_loc);
  2485. cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc);
  2486. /* If the minutia point is in a block with INVALID direction ... */
  2487. if(dmapval == INVALID_DIR)
  2488. /* Then, IGNORE the point. */
  2489. return(IGNORE);
  2490. /* If current minutia is in a HIGH CURVATURE block ... */
  2491. if(cmapval){
  2492. /* Adjust location and direction locally. */
  2493. if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc,
  2494. &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
  2495. bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){
  2496. /* Could be a system error or IGNORE minutia. */
  2497. return(ret);
  2498. }
  2499. /* Otherwise, we have our high-curvature minutia attributes. */
  2500. }
  2501. /* Otherwise, minutia is in fairly low-curvature block... */
  2502. else{
  2503. /* Get minutia direction based on current block's direction. */
  2504. idir = get_low_curvature_direction(SCAN_HORIZONTAL,
  2505. feature_patterns[feature_id].appearing, dmapval,
  2506. lfsparms->num_directions);
  2507. }
  2508. /* If current minutia is in a LOW RIDGE FLOW block ... */
  2509. if(fmapval)
  2510. reliability = MEDIUM_RELIABILITY;
  2511. else
  2512. /* Otherwise, minutia is in a block with reliable direction and */
  2513. /* binarization. */
  2514. reliability = HIGH_RELIABILITY;
  2515. /* Create a minutia object based on derived attributes. */
  2516. if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
  2517. reliability,
  2518. feature_patterns[feature_id].type,
  2519. feature_patterns[feature_id].appearing, feature_id)))
  2520. /* Return system error. */
  2521. return(ret);
  2522. /* Update the minutiae list with potential new minutia. */
  2523. ret = update_minutiae_V2(minutiae, minutia, SCAN_HORIZONTAL,
  2524. dmapval, bdata, iw, ih, lfsparms);
  2525. /* If minuitia IGNORED and not added to the minutia list ... */
  2526. if(ret == IGNORE)
  2527. /* Deallocate the minutia. */
  2528. free_minutia(minutia);
  2529. /* Otherwise, return normally. */
  2530. return(0);
  2531. }
  2532. /*************************************************************************
  2533. **************************************************************************
  2534. #cat: process_vertical_scan_minutia - Takes a minutia point that was
  2535. #cat: detected in via the vertical scan process and
  2536. #cat: adjusts its location (if necessary), determines its
  2537. #cat: direction, and (if it is not already in the minutiae
  2538. #cat: list) adds it to the list. These minutia are by nature
  2539. #cat: horizontal in orientation (orthogonal to the scan).
  2540. Input:
  2541. cx - x-pixel coord where 3rd pattern pair of mintuia was detected
  2542. cy - y-pixel coord where 3rd pattern pair of mintuia was detected
  2543. x2 - x-pixel coord where 2nd pattern pair of mintuia was detected
  2544. feature_id - type of minutia (ex. index into feature_patterns[] list)
  2545. bdata - binary image data (0==while & 1==black)
  2546. iw - width (in pixels) of image
  2547. ih - height (in pixels) of image
  2548. imapval - IMAP value associated with this image region
  2549. nmapval - NMAP value associated with this image region
  2550. lfsparms - parameters and thresholds for controlling LFS
  2551. Output:
  2552. minutiae - points to a list of detected minutia structures
  2553. Return Code:
  2554. Zero - successful completion
  2555. IGNORE - minutia is to be ignored
  2556. Negative - system error
  2557. **************************************************************************/
  2558. int process_vertical_scan_minutia(MINUTIAE *minutiae,
  2559. const int cx, const int cy,
  2560. const int y2, const int feature_id,
  2561. unsigned char *bdata, const int iw, const int ih,
  2562. const int imapval, const int nmapval,
  2563. const LFSPARMS *lfsparms)
  2564. {
  2565. MINUTIA *minutia;
  2566. int x_loc, y_loc;
  2567. int x_edge, y_edge;
  2568. int idir, ret;
  2569. /* Feature location should always point to either ending */
  2570. /* of ridge or (for bifurcations) ending of valley. */
  2571. /* So, if detected feature is APPEARING... */
  2572. if(feature_patterns[feature_id].appearing){
  2573. /* Set x location to second scan column. */
  2574. x_loc = cx+1;
  2575. /* Set x location of neighboring edge pixel to the first scan column. */
  2576. x_edge = cx;
  2577. }
  2578. /* Otherwise, feature is DISAPPEARING... */
  2579. else{
  2580. /* Set x location to first scan column. */
  2581. x_loc = cx;
  2582. /* Set x location of neighboring edge pixel to the second scan column. */
  2583. x_edge = cx+1;
  2584. }
  2585. /* Set y location of minutia point to be half way between */
  2586. /* first position of second feature pair and position of */
  2587. /* third feature pair. */
  2588. y_loc = (cy + y2)>>1;
  2589. /* Set same y location to neighboring edge pixel. */
  2590. y_edge = y_loc;
  2591. /* If current minutia is in a high-curvature block... */
  2592. if(nmapval == HIGH_CURVATURE){
  2593. /* Adjust location and direction locally. */
  2594. if((ret = adjust_high_curvature_minutia(&idir, &x_loc, &y_loc,
  2595. &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
  2596. bdata, iw, ih, minutiae, lfsparms))){
  2597. /* Could be a system error or IGNORE minutia. */
  2598. return(ret);
  2599. }
  2600. /* Otherwise, we have our high-curvature minutia attributes. */
  2601. }
  2602. /* Otherwise, minutia is in fairly low-curvature block... */
  2603. else{
  2604. /* Get minutia direction based on current IMAP value. */
  2605. idir = get_low_curvature_direction(SCAN_VERTICAL,
  2606. feature_patterns[feature_id].appearing,
  2607. imapval, lfsparms->num_directions);
  2608. }
  2609. /* Create a minutia object based on derived attributes. */
  2610. if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
  2611. DEFAULT_RELIABILITY,
  2612. feature_patterns[feature_id].type,
  2613. feature_patterns[feature_id].appearing, feature_id)))
  2614. /* Return system error. */
  2615. return(ret);
  2616. /* Update the minutiae list with potential new minutia. */
  2617. ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
  2618. /* If minuitia IGNORED and not added to the minutia list ... */
  2619. if(ret == IGNORE)
  2620. /* Deallocate the minutia. */
  2621. free_minutia(minutia);
  2622. /* Otherwise, return normally. */
  2623. return(0);
  2624. }
  2625. /*************************************************************************
  2626. **************************************************************************
  2627. #cat: process_vertical_scan_minutia_V2 - Takes a minutia point that was
  2628. #cat: detected in via the vertical scan process and
  2629. #cat: adjusts its location (if necessary), determines its
  2630. #cat: direction, and (if it is not already in the minutiae
  2631. #cat: list) adds it to the list. These minutia are by nature
  2632. #cat: horizontal in orientation (orthogonal to the scan).
  2633. Input:
  2634. cx - x-pixel coord where 3rd pattern pair of mintuia was detected
  2635. cy - y-pixel coord where 3rd pattern pair of mintuia was detected
  2636. x2 - x-pixel coord where 2nd pattern pair of mintuia was detected
  2637. feature_id - type of minutia (ex. index into feature_patterns[] list)
  2638. bdata - binary image data (0==while & 1==black)
  2639. iw - width (in pixels) of image
  2640. ih - height (in pixels) of image
  2641. pdirection_map - pixelized Direction Map
  2642. plow_flow_map - pixelized Low Ridge Flow Map
  2643. phigh_curve_map - pixelized High Curvature Map
  2644. lfsparms - parameters and thresholds for controlling LFS
  2645. Output:
  2646. minutiae - points to a list of detected minutia structures
  2647. Return Code:
  2648. Zero - successful completion
  2649. IGNORE - minutia is to be ignored
  2650. Negative - system error
  2651. **************************************************************************/
  2652. int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
  2653. const int cx, const int cy,
  2654. const int y2, const int feature_id,
  2655. unsigned char *bdata, const int iw, const int ih,
  2656. int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
  2657. const LFSPARMS *lfsparms)
  2658. {
  2659. MINUTIA *minutia;
  2660. int x_loc, y_loc;
  2661. int x_edge, y_edge;
  2662. int idir, ret;
  2663. int dmapval, fmapval, cmapval;
  2664. double reliability;
  2665. /* Feature location should always point to either ending */
  2666. /* of ridge or (for bifurcations) ending of valley. */
  2667. /* So, if detected feature is APPEARING... */
  2668. if(feature_patterns[feature_id].appearing){
  2669. /* Set x location to second scan column. */
  2670. x_loc = cx+1;
  2671. /* Set x location of neighboring edge pixel to the first scan column. */
  2672. x_edge = cx;
  2673. }
  2674. /* Otherwise, feature is DISAPPEARING... */
  2675. else{
  2676. /* Set x location to first scan column. */
  2677. x_loc = cx;
  2678. /* Set x location of neighboring edge pixel to the second scan column. */
  2679. x_edge = cx+1;
  2680. }
  2681. /* Set y location of minutia point to be half way between */
  2682. /* first position of second feature pair and position of */
  2683. /* third feature pair. */
  2684. y_loc = (cy + y2)>>1;
  2685. /* Set same y location to neighboring edge pixel. */
  2686. y_edge = y_loc;
  2687. dmapval = *(pdirection_map+(y_loc*iw)+x_loc);
  2688. fmapval = *(plow_flow_map+(y_loc*iw)+x_loc);
  2689. cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc);
  2690. /* If the minutia point is in a block with INVALID direction ... */
  2691. if(dmapval == INVALID_DIR)
  2692. /* Then, IGNORE the point. */
  2693. return(IGNORE);
  2694. /* If current minutia is in a HIGH CURVATURE block... */
  2695. if(cmapval){
  2696. /* Adjust location and direction locally. */
  2697. if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc,
  2698. &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
  2699. bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){
  2700. /* Could be a system error or IGNORE minutia. */
  2701. return(ret);
  2702. }
  2703. /* Otherwise, we have our high-curvature minutia attributes. */
  2704. }
  2705. /* Otherwise, minutia is in fairly low-curvature block... */
  2706. else{
  2707. /* Get minutia direction based on current block's direction. */
  2708. idir = get_low_curvature_direction(SCAN_VERTICAL,
  2709. feature_patterns[feature_id].appearing, dmapval,
  2710. lfsparms->num_directions);
  2711. }
  2712. /* If current minutia is in a LOW RIDGE FLOW block ... */
  2713. if(fmapval)
  2714. reliability = MEDIUM_RELIABILITY;
  2715. else
  2716. /* Otherwise, minutia is in a block with reliable direction and */
  2717. /* binarization. */
  2718. reliability = HIGH_RELIABILITY;
  2719. /* Create a minutia object based on derived attributes. */
  2720. if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
  2721. reliability,
  2722. feature_patterns[feature_id].type,
  2723. feature_patterns[feature_id].appearing, feature_id)))
  2724. /* Return system error. */
  2725. return(ret);
  2726. /* Update the minutiae list with potential new minutia. */
  2727. ret = update_minutiae_V2(minutiae, minutia, SCAN_VERTICAL,
  2728. dmapval, bdata, iw, ih, lfsparms);
  2729. /* If minuitia IGNORED and not added to the minutia list ... */
  2730. if(ret == IGNORE)
  2731. /* Deallocate the minutia. */
  2732. free_minutia(minutia);
  2733. /* Otherwise, return normally. */
  2734. return(0);
  2735. }
  2736. /*************************************************************************
  2737. **************************************************************************
  2738. #cat: adjust_high_curvature_minutia - Takes an initial minutia point detected
  2739. #cat: in a high-curvature area and adjusts its location and
  2740. #cat: direction. First, it walks and extracts the contour
  2741. #cat: of the detected feature looking for and processing any loop
  2742. #cat: discovered along the way. Once the contour is extracted,
  2743. #cat: the point of highest-curvature is determined and used to
  2744. #cat: adjust the location of the minutia point. The angle of
  2745. #cat: the line perpendicular to the tangent on the high-curvature
  2746. #cat: contour at the minutia point is used as the mintutia's
  2747. #cat: direction.
  2748. Input:
  2749. x_loc - starting x-pixel coord of feature (interior to feature)
  2750. y_loc - starting y-pixel coord of feature (interior to feature)
  2751. x_edge - x-pixel coord of corresponding edge pixel
  2752. (exterior to feature)
  2753. y_edge - y-pixel coord of corresponding edge pixel
  2754. (exterior to feature)
  2755. bdata - binary image data (0==while & 1==black)
  2756. iw - width (in pixels) of image
  2757. ih - height (in pixels) of image
  2758. lfsparms - parameters and thresholds for controlling LFS
  2759. Output:
  2760. oidir - direction of adjusted minutia point
  2761. ox_loc - adjusted x-pixel coord of feature
  2762. oy_loc - adjusted y-pixel coord of feature
  2763. ox_edge - adjusted x-pixel coord of corresponding edge pixel
  2764. oy_edge - adjusted y-pixel coord of corresponding edge pixel
  2765. minutiae - points to a list of detected minutia structures
  2766. Return Code:
  2767. Zero - minutia point processed successfully
  2768. IGNORE - minutia point is to be ignored
  2769. Negative - system error
  2770. **************************************************************************/
  2771. int adjust_high_curvature_minutia(int *oidir, int *ox_loc, int *oy_loc,
  2772. int *ox_edge, int *oy_edge,
  2773. const int x_loc, const int y_loc,
  2774. const int x_edge, const int y_edge,
  2775. unsigned char *bdata, const int iw, const int ih,
  2776. MINUTIAE *minutiae, const LFSPARMS *lfsparms)
  2777. {
  2778. int ret;
  2779. int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
  2780. int min_i;
  2781. double min_theta;
  2782. int feature_pix;
  2783. int mid_x, mid_y, mid_pix;
  2784. int idir;
  2785. int half_contour, angle_edge;
  2786. /* Set variable from parameter structure. */
  2787. half_contour = lfsparms->high_curve_half_contour;
  2788. /* Set edge length for computing contour's angle of curvature */
  2789. /* to one quarter of desired pixel length of entire contour. */
  2790. /* Ex. If half_contour==14, then contour length==29=(2X14)+1 */
  2791. /* and angle_edge==7=(14/2). */
  2792. angle_edge = half_contour>>1;
  2793. /* Get the pixel value of current feature. */
  2794. feature_pix = *(bdata + (y_loc * iw) + x_loc);
  2795. /* Extract feature's contour. */
  2796. if((ret = get_high_curvature_contour(&contour_x, &contour_y,
  2797. &contour_ex, &contour_ey, &ncontour, half_contour,
  2798. x_loc, y_loc, x_edge, y_edge, bdata, iw, ih))){
  2799. /* Returns with: */
  2800. /* 1. Successful or empty contour == 0 */
  2801. /* If contour is empty, then contour lists are not allocated. */
  2802. /* 2. Contour forms loop == LOOP_FOUND */
  2803. /* 3. Sysetm error < 0 */
  2804. /* If the contour forms a loop... */
  2805. if(ret == LOOP_FOUND){
  2806. /* If the order of the contour is clockwise, then the loops's */
  2807. /* contour pixels are outside the corresponding edge pixels. We */
  2808. /* definitely do NOT want to fill based on the feature pixel in */
  2809. /* this case, because it is OUTSIDE the loop. For now we will */
  2810. /* ignore the loop and the minutia that triggered its tracing. */
  2811. /* It is likely that other minutia on the loop will be */
  2812. /* detected that create a contour on the "inside" of the loop. */
  2813. /* There is another issue here that could be addressed ... */
  2814. /* It seems that many/multiple minutia are often detected within */
  2815. /* the same loop, which currently requires retracing the loop, */
  2816. /* locating minutia on opposite ends of the major axis of the */
  2817. /* loop, and then determining that the minutia have already been */
  2818. /* entered into the minutiae list upon processing the very first */
  2819. /* minutia detected in the loop. There is a lot of redundant */
  2820. /* work being done here! */
  2821. /* Is_loop_clockwise takes a default value to be returned if the */
  2822. /* routine is unable to determine the direction of the contour. */
  2823. /* In this case, we want to IGNORE the loop if we can't tell its */
  2824. /* direction so that we do not inappropriately fill the loop, so */
  2825. /* we are passing the default value TRUE. */
  2826. if((ret = is_loop_clockwise(contour_x, contour_y, ncontour, TRUE))){
  2827. /* Deallocate contour lists. */
  2828. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2829. /* If we had a system error... */
  2830. if(ret < 0)
  2831. /* Return the error code. */
  2832. return(ret);
  2833. /* Otherwise, loop is clockwise, so return IGNORE. */
  2834. return(IGNORE);
  2835. }
  2836. /* Otherwise, process the clockwise-ordered contour of the loop */
  2837. /* as it may contain minutia. If no minutia found, then it is */
  2838. /* filled in. */
  2839. ret = process_loop(minutiae, contour_x, contour_y,
  2840. contour_ex, contour_ey, ncontour,
  2841. bdata, iw, ih, lfsparms);
  2842. /* Returns with: */
  2843. /* 1. Successful processing of loop == 0 */
  2844. /* 2. System error < 0 */
  2845. /* Deallocate contour lists. */
  2846. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2847. /* If loop processed successfully ... */
  2848. if(ret == 0)
  2849. /* Then either a minutia pair was extracted or the loop was */
  2850. /* filled. Either way we want to IGNORE the minutia that */
  2851. /* started the whole loop processing in the beginning. */
  2852. return(IGNORE);
  2853. /* Otherwise, there was a system error. */
  2854. /* Return the resulting code. */
  2855. return(ret);
  2856. }
  2857. /* Otherwise not a loop, so get_high_curvature_contour incurred */
  2858. /* a system error. Return the error code. */
  2859. return(ret);
  2860. }
  2861. /* If contour is empty ... then contour lists were not allocated, so */
  2862. /* simply return IGNORE. The contour comes back empty when there */
  2863. /* were not a sufficient number of points found on the contour. */
  2864. if(ncontour == 0)
  2865. return(IGNORE);
  2866. /* Otherwise, there are contour points to process. */
  2867. /* Given the contour, determine the point of highest curvature */
  2868. /* (ie. forming the minimum angle between contour walls). */
  2869. if((ret = min_contour_theta(&min_i, &min_theta, angle_edge,
  2870. contour_x, contour_y, ncontour))){
  2871. /* Deallocate contour lists. */
  2872. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2873. /* Returns IGNORE or system error. Either way */
  2874. /* free the contour and return the code. */
  2875. return(ret);
  2876. }
  2877. /* If the minimum theta found along the contour is too large... */
  2878. if(min_theta >= lfsparms->max_high_curve_theta){
  2879. /* Deallocate contour lists. */
  2880. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2881. /* Reject the high-curvature minutia, and return IGNORE. */
  2882. return(IGNORE);
  2883. }
  2884. /* Test to see if interior of curvature is OK. Compute midpoint */
  2885. /* between left and right points symmetrically distant (angle_edge */
  2886. /* pixels) from the contour's point of minimum theta. */
  2887. mid_x = (contour_x[min_i-angle_edge] + contour_x[min_i+angle_edge])>>1;
  2888. mid_y = (contour_y[min_i-angle_edge] + contour_y[min_i+angle_edge])>>1;
  2889. mid_pix = *(bdata + (mid_y * iw) + mid_x);
  2890. /* If the interior pixel value is not the same as the feature's... */
  2891. if(mid_pix != feature_pix){
  2892. /* Deallocate contour lists. */
  2893. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2894. /* Reject the high-curvature minutia and return IGNORE. */
  2895. return(IGNORE);
  2896. }
  2897. /* Compute new direction based on line connecting adjusted feature */
  2898. /* location and the midpoint in the feature's interior. */
  2899. idir = line2direction(contour_x[min_i], contour_y[min_i],
  2900. mid_x, mid_y, lfsparms->num_directions);
  2901. /* Set minutia location to minimum theta position on the contour. */
  2902. *oidir = idir;
  2903. *ox_loc = contour_x[min_i];
  2904. *oy_loc = contour_y[min_i];
  2905. *ox_edge = contour_ex[min_i];
  2906. *oy_edge = contour_ey[min_i];
  2907. /* Deallocate contour buffers. */
  2908. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  2909. /*Return normally. */
  2910. return(0);
  2911. }
  2912. /*************************************************************************
  2913. **************************************************************************
  2914. #cat: adjust_high_curvature_minutia_V2 - Takes an initial minutia point
  2915. #cat: in a high-curvature area and adjusts its location and
  2916. #cat: direction. First, it walks and extracts the contour
  2917. #cat: of the detected feature looking for and processing any loop
  2918. #cat: discovered along the way. Once the contour is extracted,
  2919. #cat: the point of highest-curvature is determined and used to
  2920. #cat: adjust the location of the minutia point. The angle of
  2921. #cat: the line perpendicular to the tangent on the high-curvature
  2922. #cat: contour at the minutia point is used as the mintutia's
  2923. #cat: direction.
  2924. Input:
  2925. x_loc - starting x-pixel coord of feature (interior to feature)
  2926. y_loc - starting y-pixel coord of feature (interior to feature)
  2927. x_edge - x-pixel coord of corresponding edge pixel
  2928. (exterior to feature)
  2929. y_edge - y-pixel coord of corresponding edge pixel
  2930. (exterior to feature)
  2931. bdata - binary image data (0==while & 1==black)
  2932. iw - width (in pixels) of image
  2933. ih - height (in pixels) of image
  2934. plow_flow_map - pixelized Low Ridge Flow Map
  2935. lfsparms - parameters and thresholds for controlling LFS
  2936. Output:
  2937. oidir - direction of adjusted minutia point
  2938. ox_loc - adjusted x-pixel coord of feature
  2939. oy_loc - adjusted y-pixel coord of feature
  2940. ox_edge - adjusted x-pixel coord of corresponding edge pixel
  2941. oy_edge - adjusted y-pixel coord of corresponding edge pixel
  2942. minutiae - points to a list of detected minutia structures
  2943. Return Code:
  2944. Zero - minutia point processed successfully
  2945. IGNORE - minutia point is to be ignored
  2946. Negative - system error
  2947. **************************************************************************/
  2948. int adjust_high_curvature_minutia_V2(int *oidir, int *ox_loc, int *oy_loc,
  2949. int *ox_edge, int *oy_edge,
  2950. const int x_loc, const int y_loc,
  2951. const int x_edge, const int y_edge,
  2952. unsigned char *bdata, const int iw, const int ih,
  2953. int *plow_flow_map, MINUTIAE *minutiae, const LFSPARMS *lfsparms)
  2954. {
  2955. int ret;
  2956. int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
  2957. int min_i;
  2958. double min_theta;
  2959. int feature_pix;
  2960. int mid_x, mid_y, mid_pix;
  2961. int idir;
  2962. int half_contour, angle_edge;
  2963. /* Set variable from parameter structure. */
  2964. half_contour = lfsparms->high_curve_half_contour;
  2965. /* Set edge length for computing contour's angle of curvature */
  2966. /* to one quarter of desired pixel length of entire contour. */
  2967. /* Ex. If half_contour==14, then contour length==29=(2X14)+1 */
  2968. /* and angle_edge==7=(14/2). */
  2969. angle_edge = half_contour>>1;
  2970. /* Get the pixel value of current feature. */
  2971. feature_pix = *(bdata + (y_loc * iw) + x_loc);
  2972. /* Extract feature's contour. */
  2973. if((ret = get_high_curvature_contour(&contour_x, &contour_y,
  2974. &contour_ex, &contour_ey, &ncontour, half_contour,
  2975. x_loc, y_loc, x_edge, y_edge, bdata, iw, ih))){
  2976. /* Returns with: */
  2977. /* 1. Successful or empty contour == 0 */
  2978. /* If contour is empty, then contour lists are not allocated. */
  2979. /* 2. Contour forms loop == LOOP_FOUND */
  2980. /* 3. Sysetm error < 0 */
  2981. /* If the contour forms a loop... */
  2982. if(ret == LOOP_FOUND){
  2983. /* If the order of the contour is clockwise, then the loops's */
  2984. /* contour pixels are outside the corresponding edge pixels. We */
  2985. /* definitely do NOT want to fill based on the feature pixel in */
  2986. /* this case, because it is OUTSIDE the loop. For now we will */
  2987. /* ignore the loop and the minutia that triggered its tracing. */
  2988. /* It is likely that other minutia on the loop will be */
  2989. /* detected that create a contour on the "inside" of the loop. */
  2990. /* There is another issue here that could be addressed ... */
  2991. /* It seems that many/multiple minutia are often detected within */
  2992. /* the same loop, which currently requires retracing the loop, */
  2993. /* locating minutia on opposite ends of the major axis of the */
  2994. /* loop, and then determining that the minutia have already been */
  2995. /* entered into the minutiae list upon processing the very first */
  2996. /* minutia detected in the loop. There is a lot of redundant */
  2997. /* work being done here! */
  2998. /* Is_loop_clockwise takes a default value to be returned if the */
  2999. /* routine is unable to determine the direction of the contour. */
  3000. /* In this case, we want to IGNORE the loop if we can't tell its */
  3001. /* direction so that we do not inappropriately fill the loop, so */
  3002. /* we are passing the default value TRUE. */
  3003. if((ret = is_loop_clockwise(contour_x, contour_y, ncontour, TRUE))){
  3004. /* Deallocate contour lists. */
  3005. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3006. /* If we had a system error... */
  3007. if(ret < 0)
  3008. /* Return the error code. */
  3009. return(ret);
  3010. /* Otherwise, loop is clockwise, so return IGNORE. */
  3011. return(IGNORE);
  3012. }
  3013. /* Otherwise, process the clockwise-ordered contour of the loop */
  3014. /* as it may contain minutia. If no minutia found, then it is */
  3015. /* filled in. */
  3016. ret = process_loop_V2(minutiae, contour_x, contour_y,
  3017. contour_ex, contour_ey, ncontour,
  3018. bdata, iw, ih, plow_flow_map, lfsparms);
  3019. /* Returns with: */
  3020. /* 1. Successful processing of loop == 0 */
  3021. /* 2. System error < 0 */
  3022. /* Deallocate contour lists. */
  3023. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3024. /* If loop processed successfully ... */
  3025. if(ret == 0)
  3026. /* Then either a minutia pair was extracted or the loop was */
  3027. /* filled. Either way we want to IGNORE the minutia that */
  3028. /* started the whole loop processing in the beginning. */
  3029. return(IGNORE);
  3030. /* Otherwise, there was a system error. */
  3031. /* Return the resulting code. */
  3032. return(ret);
  3033. }
  3034. /* Otherwise not a loop, so get_high_curvature_contour incurred */
  3035. /* a system error. Return the error code. */
  3036. return(ret);
  3037. }
  3038. /* If contour is empty ... then contour lists were not allocated, so */
  3039. /* simply return IGNORE. The contour comes back empty when there */
  3040. /* were not a sufficient number of points found on the contour. */
  3041. if(ncontour == 0)
  3042. return(IGNORE);
  3043. /* Otherwise, there are contour points to process. */
  3044. /* Given the contour, determine the point of highest curvature */
  3045. /* (ie. forming the minimum angle between contour walls). */
  3046. if((ret = min_contour_theta(&min_i, &min_theta, angle_edge,
  3047. contour_x, contour_y, ncontour))){
  3048. /* Deallocate contour lists. */
  3049. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3050. /* Returns IGNORE or system error. Either way */
  3051. /* free the contour and return the code. */
  3052. return(ret);
  3053. }
  3054. /* If the minimum theta found along the contour is too large... */
  3055. if(min_theta >= lfsparms->max_high_curve_theta){
  3056. /* Deallocate contour lists. */
  3057. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3058. /* Reject the high-curvature minutia, and return IGNORE. */
  3059. return(IGNORE);
  3060. }
  3061. /* Test to see if interior of curvature is OK. Compute midpoint */
  3062. /* between left and right points symmetrically distant (angle_edge */
  3063. /* pixels) from the contour's point of minimum theta. */
  3064. mid_x = (contour_x[min_i-angle_edge] + contour_x[min_i+angle_edge])>>1;
  3065. mid_y = (contour_y[min_i-angle_edge] + contour_y[min_i+angle_edge])>>1;
  3066. mid_pix = *(bdata + (mid_y * iw) + mid_x);
  3067. /* If the interior pixel value is not the same as the feature's... */
  3068. if(mid_pix != feature_pix){
  3069. /* Deallocate contour lists. */
  3070. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3071. /* Reject the high-curvature minutia and return IGNORE. */
  3072. return(IGNORE);
  3073. }
  3074. /* Compute new direction based on line connecting adjusted feature */
  3075. /* location and the midpoint in the feature's interior. */
  3076. idir = line2direction(contour_x[min_i], contour_y[min_i],
  3077. mid_x, mid_y, lfsparms->num_directions);
  3078. /* Set minutia location to minimum theta position on the contour. */
  3079. *oidir = idir;
  3080. *ox_loc = contour_x[min_i];
  3081. *oy_loc = contour_y[min_i];
  3082. *ox_edge = contour_ex[min_i];
  3083. *oy_edge = contour_ey[min_i];
  3084. /* Deallocate contour buffers. */
  3085. free_contour(contour_x, contour_y, contour_ex, contour_ey);
  3086. /*Return normally. */
  3087. return(0);
  3088. }
  3089. /*************************************************************************
  3090. **************************************************************************
  3091. #cat: get_low_curvature_direction - Converts a bi-direcitonal IMAP direction
  3092. #cat: (based on a semi-circle) to a uni-directional value covering
  3093. #cat: a full circle based on the scan orientation used to detect
  3094. #cat: a minutia feature (horizontal or vertical) and whether the
  3095. #cat: detected minutia is appearing or disappearing.
  3096. Input:
  3097. scan_dir - designates the feature scan orientation
  3098. appearing - designates the minutia as appearing or disappearing
  3099. imapval - IMAP block direction
  3100. ndirs - number of IMAP directions (in semicircle)
  3101. Return Code:
  3102. New direction - bi-directonal integer direction on full circle
  3103. *************************************************************************/
  3104. int get_low_curvature_direction(const int scan_dir, const int appearing,
  3105. const int imapval, const int ndirs)
  3106. {
  3107. int idir;
  3108. /* Start direction out with IMAP value. */
  3109. idir = imapval;
  3110. /* NOTE! */
  3111. /* The logic in this routine should hold whether for ridge endings */
  3112. /* or for bifurcations. The examples in the comments show ridge */
  3113. /* ending conditions only. */
  3114. /* CASE I : Ridge flow in Quadrant I; directions [0..8] */
  3115. if(imapval <= (ndirs>>1)){
  3116. /* I.A: HORIZONTAL scan */
  3117. if(scan_dir == SCAN_HORIZONTAL){
  3118. /* I.A.1: Appearing Minutia */
  3119. if(appearing){
  3120. /* Ex. 0 0 0 */
  3121. /* 0 1 0 */
  3122. /* ? ? */
  3123. /* Ridge flow is up and to the right, whereas */
  3124. /* actual ridge is running down and to the */
  3125. /* left. */
  3126. /* Thus: HORIZONTAL : appearing : should be */
  3127. /* OPPOSITE the ridge flow direction. */
  3128. idir += ndirs;
  3129. }
  3130. /* Otherwise: */
  3131. /* I.A.2: Disappearing Minutia */
  3132. /* Ex. ? ? */
  3133. /* 0 1 0 */
  3134. /* 0 0 0 */
  3135. /* Ridge flow is up and to the right, which */
  3136. /* should be SAME direction from which ridge */
  3137. /* is projecting. */
  3138. /* Thus: HORIZONTAL : disappearing : should */
  3139. /* be the same as ridge flow direction. */
  3140. } /* End if HORIZONTAL scan */
  3141. /* Otherwise: */
  3142. /* I.B: VERTICAL scan */
  3143. else{
  3144. /* I.B.1: Disappearing Minutia */
  3145. if(!appearing){
  3146. /* Ex. 0 0 */
  3147. /* ? 1 0 */
  3148. /* ? 0 0 */
  3149. /* Ridge flow is up and to the right, whereas */
  3150. /* actual ridge is projecting down and to the */
  3151. /* left. */
  3152. /* Thus: VERTICAL : disappearing : should be */
  3153. /* OPPOSITE the ridge flow direction. */
  3154. idir += ndirs;
  3155. }
  3156. /* Otherwise: */
  3157. /* I.B.2: Appearing Minutia */
  3158. /* Ex. 0 0 ? */
  3159. /* 0 1 ? */
  3160. /* 0 0 */
  3161. /* Ridge flow is up and to the right, which */
  3162. /* should be SAME direction the ridge is */
  3163. /* running. */
  3164. /* Thus: VERTICAL : appearing : should be */
  3165. /* be the same as ridge flow direction. */
  3166. } /* End else VERTICAL scan */
  3167. } /* End if Quadrant I */
  3168. /* Otherwise: */
  3169. /* CASE II : Ridge flow in Quadrant II; directions [9..15] */
  3170. else{
  3171. /* II.A: HORIZONTAL scan */
  3172. if(scan_dir == SCAN_HORIZONTAL){
  3173. /* II.A.1: Disappearing Minutia */
  3174. if(!appearing){
  3175. /* Ex. ? ? */
  3176. /* 0 1 0 */
  3177. /* 0 0 0 */
  3178. /* Ridge flow is down and to the right, */
  3179. /* whereas actual ridge is running up and to */
  3180. /* the left. */
  3181. /* Thus: HORIZONTAL : disappearing : should */
  3182. /* be OPPOSITE the ridge flow direction.*/
  3183. idir += ndirs;
  3184. }
  3185. /* Otherwise: */
  3186. /* II.A.2: Appearing Minutia */
  3187. /* Ex. 0 0 0 */
  3188. /* 0 1 0 */
  3189. /* ? ? */
  3190. /* Ridge flow is down and to the right, which */
  3191. /* should be same direction from which ridge */
  3192. /* is projecting. */
  3193. /* Thus: HORIZONTAL : appearing : should be */
  3194. /* the SAME as ridge flow direction. */
  3195. } /* End if HORIZONTAL scan */
  3196. /* Otherwise: */
  3197. /* II.B: VERTICAL scan */
  3198. else{
  3199. /* II.B.1: Disappearing Minutia */
  3200. if(!appearing){
  3201. /* Ex. ? 0 0 */
  3202. /* ? 1 0 */
  3203. /* 0 0 */
  3204. /* Ridge flow is down and to the right, */
  3205. /* whereas actual ridge is running up and to */
  3206. /* the left. */
  3207. /* Thus: VERTICAL : disappearing : should be */
  3208. /* OPPOSITE the ridge flow direction. */
  3209. idir += ndirs;
  3210. }
  3211. /* Otherwise: */
  3212. /* II.B.2: Appearing Minutia */
  3213. /* Ex. 0 0 */
  3214. /* 0 1 ? */
  3215. /* 0 0 ? */
  3216. /* Ridge flow is down and to the right, which */
  3217. /* should be same direction the ridge is */
  3218. /* projecting. */
  3219. /* Thus: VERTICAL : appearing : should be */
  3220. /* be the SAME as ridge flow direction. */
  3221. } /* End else VERTICAL scan */
  3222. } /* End else Quadrant II */
  3223. /* Return resulting direction on range [0..31]. */
  3224. return(idir);
  3225. }