1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580 |
- /*******************************************************************************
- License:
- This software and/or related materials was developed at the National Institute
- of Standards and Technology (NIST) by employees of the Federal Government
- in the course of their official duties. Pursuant to title 17 Section 105
- of the United States Code, this software is not subject to copyright
- protection and is in the public domain.
- This software and/or related materials have been determined to be not subject
- to the EAR (see Part 734.3 of the EAR for exact details) because it is
- a publicly available technology and software, and is freely distributed
- to any interested party with no licensing requirements. Therefore, it is
- permissible to distribute this software as a free download from the internet.
- Disclaimer:
- This software and/or related materials was developed to promote biometric
- standards and biometric technology testing for the Federal Government
- in accordance with the USA PATRIOT Act and the Enhanced Border Security
- and Visa Entry Reform Act. Specific hardware and software products identified
- in this software were used in order to perform the software development.
- In no case does such identification imply recommendation or endorsement
- by the National Institute of Standards and Technology, nor does it imply that
- the products and equipment identified are necessarily the best available
- for the purpose.
- This software and/or related materials are provided "AS-IS" without warranty
- of any kind including NO WARRANTY OF PERFORMANCE, MERCHANTABILITY,
- NO WARRANTY OF NON-INFRINGEMENT OF ANY 3RD PARTY INTELLECTUAL PROPERTY
- or FITNESS FOR A PARTICULAR PURPOSE or for any purpose whatsoever, for the
- licensed product, however used. In no event shall NIST be liable for any
- damages and/or costs, including but not limited to incidental or consequential
- damages of any kind, including economic damage or injury to property and lost
- profits, regardless of whether NIST shall be advised, have reason to know,
- or in fact shall know of the possibility.
- By using this software, you agree to bear all risk relating to quality,
- use and performance of the software and/or related materials. You agree
- to hold the Government harmless from any claim arising from your use
- of the software.
- *******************************************************************************/
- /***********************************************************************
- LIBRARY: LFS - NIST Latent Fingerprint System
- FILE: MINUTIA.C
- AUTHOR: Michael D. Garris
- DATE: 05/11/1999
- UPDATED: 10/04/1999 Version 2 by MDG
- UPDATED: 09/13/2004
- Contains routines responsible for detecting initial minutia
- points as part of the NIST Latent Fingerprint System (LFS).
- ***********************************************************************
- ROUTINES:
- alloc_minutiae()
- realloc_minutiae()
- detect_minutiae()
- detect_minutiae_V2()
- update_minutiae()
- update_minutiae_V2()
- sort_minutiae_y_x()
- sort_minutiae_x_y()
- rm_dup_minutiae()
- dump_minutiae()
- dump_minutiae_pts()
- dump_reliable_minutiae_pts()
- create_minutia()
- free_minutiae()
- free_minutia()
- remove_minutia()
- join_minutia()
- minutia_type()
- is_minutia_appearing()
- choose_scan_direction()
- scan4minutiae()
- scan4minutiae_horizontally()
- scan4minutiae_horizontally_V2()
- scan4minutiae_vertically()
- scan4minutiae_vertically_V2()
- rescan4minutiae_horizontally()
- rescan4minutiae_vertically()
- rescan_partial_horizontally()
- rescan_partial_vertically()
- get_nbr_block_index()
- adjust_horizontal_rescan()
- adjust_vertical_rescan()
- process_horizontal_scan_minutia()
- process_horizontal_scan_minutia_V2()
- process_vertical_scan_minutia()
- process_vertical_scan_minutia_V2()
- adjust_high_curvature_minutia()
- adjust_high_curvature_minutia_V2()
- get_low_curvature_direction()
- ***********************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include "lfs.h"
- /*************************************************************************
- **************************************************************************
- #cat: alloc_minutiae - Allocates and initializes a minutia list based on the
- #cat: specified maximum number of minutiae to be detected.
- Input:
- max_minutiae - number of minutia to be allocated in list
- Output:
- ominutiae - points to the allocated minutiae list
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int alloc_minutiae(MINUTIAE **ominutiae, const int max_minutiae)
- {
- MINUTIAE *minutiae;
- minutiae = (MINUTIAE *)malloc(sizeof(MINUTIAE));
- if(minutiae == (MINUTIAE *)NULL){
- fprintf(stderr, "ERROR : alloc_minutiae : malloc : minutiae\n");
- exit(-430);
- }
- minutiae->list = (MINUTIA **)malloc(max_minutiae * sizeof(MINUTIA *));
- if(minutiae->list == (MINUTIA **)NULL){
- fprintf(stderr, "ERROR : alloc_minutiae : malloc : minutiae->list\n");
- exit(-431);
- }
- minutiae->alloc = max_minutiae;
- minutiae->num = 0;
- *ominutiae = minutiae;
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: realloc_minutiae - Reallocates a previously allocated minutia list
- #cat: extending its allocated length based on the specified
- #cat: increment.
- Input:
- minutiae - previously allocated list of minutiae points
- max_minutiae - number of minutia to be allocated in list
- Output:
- minutiae - extended list of minutiae points
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int realloc_minutiae(MINUTIAE *minutiae, const int incr_minutiae)
- {
- minutiae->alloc += incr_minutiae;
- minutiae->list = (MINUTIA **)realloc(minutiae->list,
- minutiae->alloc * sizeof(MINUTIA *));
- if(minutiae->list == (MINUTIA **)NULL){
- fprintf(stderr, "ERROR : realloc_minutiae : realloc : minutiae->list\n");
- exit(-432);
- }
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: detect_minutiae - Takes a binary image and its associated IMAP and
- #cat: NMAP matrices and scans each image block for potential
- #cat: minutia points.
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int detect_minutiae(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap, const int mw, const int mh,
- const LFSPARMS *lfsparms)
- {
- int blk_i, blk_x, blk_y;
- int scan_x, scan_y, scan_w, scan_h;
- int scan_dir;
- int ret;
- /* Start with first block in IMAP. */
- blk_i = 0;
- /* Start with first scan line in image. */
- scan_y = 0;
- /* Foreach row of blocks in IMAP... */
- for(blk_y = 0; blk_y < mh; blk_y++){
- /* Reset to beginning of new block row. */
- scan_x = 0;
- /* Foreach block in current IMAP row... */
- for(blk_x = 0; blk_x < mw; blk_x++){
- /* If IMAP is VALID ... */
- if(imap[blk_i] != INVALID_DIR){
- /* Choose the feature scan direction based on the block's */
- /* VALID IMAP direction. The scan direction will either */
- /* be HORIZONTAL or VERTICAL. */
- scan_dir = choose_scan_direction(imap[blk_i],
- lfsparms->num_directions);
- /* Set width of scan region. The image may not be an even */
- /* multiple of "blocksize" in width and height, so we must */
- /* account for this. */
- /* Bump right by "blocksize" pixels, but not beyond the */
- /* image boundary. */
- scan_w = min(scan_x+lfsparms->blocksize, iw);
- /* Make the resulting width relative to the region's starting */
- /* x-pixel column. */
- scan_w -= scan_x;
- /* Bump down by "blocksize" pixels, but not beyond the */
- /* image boundary. */
- scan_h = min(scan_y+lfsparms->blocksize, ih);
- /* Make the resulting height relative to the region's starting */
- /* y-pixel row. */
- scan_h -= scan_y;
- /* Scan the defined region for minutia features. */
- if((ret = scan4minutiae(minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, scan_dir,
- lfsparms))){
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- }
- } /* Otherwise, IMAP is INVALID, so ignore the block. This seems */
- /* quite drastic! */
- /* Advance to the next IMAP block in the row in the image. */
- scan_x += lfsparms->blocksize;
- /* Advance to the next IMAP block in the row. */
- blk_i++;
- } /* End foreach blk_x */
- /* Advance to the next IMAP row in the image. */
- scan_y += lfsparms->blocksize;
- } /* End foreach blk_y */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: detect_minutiae_V2 - Takes a binary image and its associated
- #cat: Direction and Low Flow Maps and scans each image block
- #cat: with valid direction for minutia points. Minutia points
- #cat: detected in LOW FLOW blocks are set with lower reliability.
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- direction_map - map of image blocks containing directional ridge flow
- low_flow_map - map of image blocks flagged as LOW RIDGE FLOW
- high_curve_map - map of image blocks flagged as HIGH CURVATURE
- mw - width (in blocks) of the maps
- mh - height (in blocks) of the maps
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int detect_minutiae_V2(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- int *direction_map, int *low_flow_map, int *high_curve_map,
- const int mw, const int mh,
- const LFSPARMS *lfsparms)
- {
- int ret;
- int *pdirection_map, *plow_flow_map, *phigh_curve_map;
- /* Pixelize the maps by assigning block values to individual pixels. */
- if((ret = pixelize_map(&pdirection_map, iw, ih, direction_map, mw, mh,
- lfsparms->blocksize))){
- return(ret);
- }
- if((ret = pixelize_map(&plow_flow_map, iw, ih, low_flow_map, mw, mh,
- lfsparms->blocksize))){
- free(pdirection_map);
- return(ret);
- }
- if((ret = pixelize_map(&phigh_curve_map, iw, ih, high_curve_map, mw, mh,
- lfsparms->blocksize))){
- free(pdirection_map);
- free(plow_flow_map);
- return(ret);
- }
- if((ret = scan4minutiae_horizontally_V2(minutiae, bdata, iw, ih,
- pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){
- free(pdirection_map);
- free(plow_flow_map);
- free(phigh_curve_map);
- return(ret);
- }
- if((ret = scan4minutiae_vertically_V2(minutiae, bdata, iw, ih,
- pdirection_map, plow_flow_map, phigh_curve_map, lfsparms))){
- free(pdirection_map);
- free(plow_flow_map);
- free(phigh_curve_map);
- return(ret);
- }
- /* Deallocate working memories. */
- free(pdirection_map);
- free(plow_flow_map);
- free(phigh_curve_map);
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: update_minutiae - Takes a detected minutia point and (if it is not
- #cat: determined to already be in the minutiae list) adds it to
- #cat: the list.
- Input:
- minutia - minutia structure for detected point
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - minutia added to successfully added to minutiae list
- IGNORE - minutia is to be ignored (already in the minutiae list)
- Negative - system error
- **************************************************************************/
- int update_minutiae(MINUTIAE *minutiae, MINUTIA *minutia,
- unsigned char *bdata, const int iw, const int ih,
- const LFSPARMS *lfsparms)
- {
- int i, ret, dy, dx, delta_dir;
- int qtr_ndirs, full_ndirs;
- /* Check to see if minutiae list is full ... if so, then extend */
- /* the length of the allocated list of minutia points. */
- if(minutiae->num >= minutiae->alloc){
- if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE)))
- return(ret);
- }
- /* Otherwise, there is still room for more minutia. */
- /* Compute quarter of possible directions in a semi-circle */
- /* (ie. 45 degrees). */
- qtr_ndirs = lfsparms->num_directions>>2;
- /* Compute number of directions in full circle. */
- full_ndirs = lfsparms->num_directions<<1;
- /* Is the minutiae list empty? */
- if(minutiae->num > 0){
- /* Foreach minutia stored in the list... */
- for(i = 0; i < minutiae->num; i++){
- /* If x distance between new minutia and current list minutia */
- /* are sufficiently close... */
- dx = abs(minutiae->list[i]->x - minutia->x);
- if(dx < lfsparms->max_minutia_delta){
- /* If y distance between new minutia and current list minutia */
- /* are sufficiently close... */
- dy = abs(minutiae->list[i]->y - minutia->y);
- if(dy < lfsparms->max_minutia_delta){
- /* If new minutia and current list minutia are same type... */
- if(minutiae->list[i]->type == minutia->type){
- /* Test to see if minutiae have similar directions. */
- /* Take minimum of computed inner and outer */
- /* direction differences. */
- delta_dir = abs(minutiae->list[i]->direction -
- minutia->direction);
- delta_dir = min(delta_dir, full_ndirs-delta_dir);
- /* If directional difference is <= 45 degrees... */
- if(delta_dir <= qtr_ndirs){
- /* If new minutia and current list minutia share */
- /* the same point... */
- if((dx==0) && (dy==0)){
- /* Then the minutiae match, so don't add the new one */
- /* to the list. */
- return(IGNORE);
- }
- /* Othewise, check if they share the same contour. */
- /* Start by searching "max_minutia_delta" steps */
- /* clockwise. */
- /* If new minutia point found on contour... */
- if(search_contour(minutia->x, minutia->y,
- lfsparms->max_minutia_delta,
- minutiae->list[i]->x, minutiae->list[i]->y,
- minutiae->list[i]->ex, minutiae->list[i]->ey,
- SCAN_CLOCKWISE, bdata, iw, ih)){
- /* Consider the new minutia to be the same as the */
- /* current list minutia, so don't add the new one */
- /* to the list. */
- return(IGNORE);
- }
- /* Now search "max_minutia_delta" steps counter- */
- /* clockwise along contour. */
- /* If new minutia point found on contour... */
- if(search_contour(minutia->x, minutia->y,
- lfsparms->max_minutia_delta,
- minutiae->list[i]->x, minutiae->list[i]->y,
- minutiae->list[i]->ex, minutiae->list[i]->ey,
- SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){
- /* Consider the new minutia to be the same as the */
- /* current list minutia, so don't add the new one */
- /* to the list. */
- return(IGNORE);
- }
- /* Otherwise, new minutia and current list minutia do */
- /* not share the same contour, so although they are */
- /* similar in type and location, treat them as 2 */
- /* different minutia. */
- } /* Otherwise, directions are too different. */
- } /* Otherwise, minutiae are different type. */
- } /* Otherwise, minutiae too far apart in Y. */
- } /* Otherwise, minutiae too far apart in X. */
- } /* End FOR minutia in list. */
- } /* Otherwise, minutiae list is empty. */
- /* Otherwise, assume new minutia is not in the list, so add it. */
- minutiae->list[minutiae->num] = minutia;
- (minutiae->num)++;
- /* New minutia was successfully added to the list. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: update_minutiae_V2 - Takes a detected minutia point and (if it is not
- #cat: determined to already be in the minutiae list or the
- #cat: new point is determined to be "more compatible") adds
- #cat: it to the list.
- Input:
- minutia - minutia structure for detected point
- scan_dir - orientation of scan when minutia was detected
- dmapval - directional ridge flow of block minutia is in
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - minutia added to successfully added to minutiae list
- IGNORE - minutia is to be ignored (already in the minutiae list)
- Negative - system error
- **************************************************************************/
- int update_minutiae_V2(MINUTIAE *minutiae, MINUTIA *minutia,
- const int scan_dir, const int dmapval,
- unsigned char *bdata, const int iw, const int ih,
- const LFSPARMS *lfsparms)
- {
- int i, ret, dy, dx, delta_dir;
- int qtr_ndirs, full_ndirs;
- int map_scan_dir;
- /* Check to see if minutiae list is full ... if so, then extend */
- /* the length of the allocated list of minutia points. */
- if(minutiae->num >= minutiae->alloc){
- if((ret = realloc_minutiae(minutiae, MAX_MINUTIAE)))
- return(ret);
- }
- /* Otherwise, there is still room for more minutia. */
- /* Compute quarter of possible directions in a semi-circle */
- /* (ie. 45 degrees). */
- qtr_ndirs = lfsparms->num_directions>>2;
- /* Compute number of directions in full circle. */
- full_ndirs = lfsparms->num_directions<<1;
- /* Is the minutiae list empty? */
- if(minutiae->num > 0){
- /* Foreach minutia stored in the list (in reverse order) ... */
- for(i = minutiae->num-1; i >= 0; i--){
- /* If x distance between new minutia and current list minutia */
- /* are sufficiently close... */
- dx = abs(minutiae->list[i]->x - minutia->x);
- if(dx < lfsparms->max_minutia_delta){
- /* If y distance between new minutia and current list minutia */
- /* are sufficiently close... */
- dy = abs(minutiae->list[i]->y - minutia->y);
- if(dy < lfsparms->max_minutia_delta){
- /* If new minutia and current list minutia are same type... */
- if(minutiae->list[i]->type == minutia->type){
- /* Test to see if minutiae have similar directions. */
- /* Take minimum of computed inner and outer */
- /* direction differences. */
- delta_dir = abs(minutiae->list[i]->direction -
- minutia->direction);
- delta_dir = min(delta_dir, full_ndirs-delta_dir);
- /* If directional difference is <= 45 degrees... */
- if(delta_dir <= qtr_ndirs){
- /* If new minutia and current list minutia share */
- /* the same point... */
- if((dx==0) && (dy==0)){
- /* Then the minutiae match, so don't add the new one */
- /* to the list. */
- return(IGNORE);
- }
- /* Othewise, check if they share the same contour. */
- /* Start by searching "max_minutia_delta" steps */
- /* clockwise. */
- /* If new minutia point found on contour... */
- if(search_contour(minutia->x, minutia->y,
- lfsparms->max_minutia_delta,
- minutiae->list[i]->x, minutiae->list[i]->y,
- minutiae->list[i]->ex, minutiae->list[i]->ey,
- SCAN_CLOCKWISE, bdata, iw, ih) ||
- search_contour(minutia->x, minutia->y,
- lfsparms->max_minutia_delta,
- minutiae->list[i]->x, minutiae->list[i]->y,
- minutiae->list[i]->ex, minutiae->list[i]->ey,
- SCAN_COUNTER_CLOCKWISE, bdata, iw, ih)){
- /* If new minutia has VALID block direction ... */
- if(dmapval >= 0){
- /* Derive feature scan direction compatible */
- /* with VALID direction. */
- map_scan_dir = choose_scan_direction(dmapval,
- lfsparms->num_directions);
- /* If map scan direction compatible with scan */
- /* direction in which new minutia was found ... */
- if(map_scan_dir == scan_dir){
- /* Then choose the new minutia over the one */
- /* currently in the list. */
- if((ret = remove_minutia(i, minutiae))){
- return(ret);
- }
- /* Continue on ... */
- }
- else
- /* Othersize, scan directions not compatible...*/
- /* so choose to keep the current minutia in */
- /* the list and ignore the new one. */
- return(IGNORE);
- }
- else{
- /* Otherwise, no reason to believe new minutia */
- /* is any better than the current one in the list,*/
- /* so consider the new minutia to be the same as */
- /* the current list minutia, and don't add the new*/
- /* one to the list. */
- return(IGNORE);
- }
- }
- /* Otherwise, new minutia and current list minutia do */
- /* not share the same contour, so although they are */
- /* similar in type and location, treat them as 2 */
- /* different minutia. */
- } /* Otherwise, directions are too different. */
- } /* Otherwise, minutiae are different type. */
- } /* Otherwise, minutiae too far apart in Y. */
- } /* Otherwise, minutiae too far apart in X. */
- } /* End FOR minutia in list. */
- } /* Otherwise, minutiae list is empty. */
- /* Otherwise, assume new minutia is not in the list, or those that */
- /* were close neighbors were selectively removed, so add it. */
- minutiae->list[minutiae->num] = minutia;
- (minutiae->num)++;
- /* New minutia was successfully added to the list. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: sort_minutiae_y_x - Takes a list of minutia points and sorts them
- #cat: top-to-bottom and then left-to-right.
- Input:
- minutiae - list of minutiae
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- Output:
- minutiae - list of sorted minutiae
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int sort_minutiae_y_x(MINUTIAE *minutiae, const int iw, const int ih)
- {
- int *ranks, *order;
- int i, ret;
- MINUTIA **newlist;
- /* Allocate a list of integers to hold 1-D image pixel offsets */
- /* for each of the 2-D minutia coordinate points. */
- ranks = (int *)malloc(minutiae->num * sizeof(int));
- if(ranks == (int *)NULL){
- fprintf(stderr, "ERROR : sort_minutiae_y_x : malloc : ranks\n");
- return(-310);
- }
- /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */
- for(i = 0; i < minutiae->num; i++)
- ranks[i] = (minutiae->list[i]->y * iw) + minutiae->list[i]->x;
- /* Get sorted order of minutiae. */
- if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){
- free(ranks);
- return(ret);
- }
- /* Allocate new MINUTIA list to hold sorted minutiae. */
- newlist = (MINUTIA **)malloc(minutiae->num * sizeof(MINUTIA *));
- if(newlist == (MINUTIA **)NULL){
- free(ranks);
- free(order);
- fprintf(stderr, "ERROR : sort_minutiae_y_x : malloc : newlist\n");
- return(-311);
- }
- /* Put minutia into sorted order in new list. */
- for(i = 0; i < minutiae->num; i++)
- newlist[i] = minutiae->list[order[i]];
- /* Deallocate non-sorted list of minutia pointers. */
- free(minutiae->list);
- /* Assign new sorted list of minutia to minutiae list. */
- minutiae->list = newlist;
- /* Free the working memories supporting the sort. */
- free(order);
- free(ranks);
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: sort_minutiae_x_y - Takes a list of minutia points and sorts them
- #cat: left-to-right and then top-to-bottom.
- Input:
- minutiae - list of minutiae
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- Output:
- minutiae - list of sorted minutiae
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int sort_minutiae_x_y(MINUTIAE *minutiae, const int iw, const int ih)
- {
- int *ranks, *order;
- int i, ret;
- MINUTIA **newlist;
- /* Allocate a list of integers to hold 1-D image pixel offsets */
- /* for each of the 2-D minutia coordinate points. */
- ranks = (int *)malloc(minutiae->num * sizeof(int));
- if(ranks == (int *)NULL){
- fprintf(stderr, "ERROR : sort_minutiae_x_y : malloc : ranks\n");
- return(-440);
- }
- /* Compute 1-D image pixel offsets form 2-D minutia coordinate points. */
- for(i = 0; i < minutiae->num; i++)
- ranks[i] = (minutiae->list[i]->x * iw) + minutiae->list[i]->y;
- /* Get sorted order of minutiae. */
- if((ret = sort_indices_int_inc(&order, ranks, minutiae->num))){
- free(ranks);
- return(ret);
- }
- /* Allocate new MINUTIA list to hold sorted minutiae. */
- newlist = (MINUTIA **)malloc(minutiae->num * sizeof(MINUTIA *));
- if(newlist == (MINUTIA **)NULL){
- free(ranks);
- free(order);
- fprintf(stderr, "ERROR : sort_minutiae_x_y : malloc : newlist\n");
- return(-441);
- }
- /* Put minutia into sorted order in new list. */
- for(i = 0; i < minutiae->num; i++)
- newlist[i] = minutiae->list[order[i]];
- /* Deallocate non-sorted list of minutia pointers. */
- free(minutiae->list);
- /* Assign new sorted list of minutia to minutiae list. */
- minutiae->list = newlist;
- /* Free the working memories supporting the sort. */
- free(order);
- free(ranks);
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: rm_dup_minutiae - Takes a list of minutiae sorted in some adjacent order
- #cat: and detects and removes redundant minutia that have the
- #cat: same exact pixel coordinate locations (even if other
- #cat: attributes may differ).
- Input:
- mintuiae - list of sorted minutiae
- Output:
- mintuiae - list of sorted minutiae with duplicates removed
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int rm_dup_minutiae(MINUTIAE *minutiae)
- {
- int i, ret;
- MINUTIA *minutia1, *minutia2;
- /* Work backward from the end of the list of minutiae. This way */
- /* we can selectively remove minutia from the list and not cause */
- /* problems with keeping track of current indices. */
- for(i = minutiae->num-1; i > 0; i--){
- minutia1 = minutiae->list[i];
- minutia2 = minutiae->list[i-1];
- /* If minutia pair has identical coordinates ... */
- if((minutia1->x == minutia2->x) &&
- (minutia1->y == minutia2->y)){
- /* Remove the 2nd minutia from the minutiae list. */
- if((ret = remove_minutia(i-1, minutiae)))
- return(ret);
- /* The first minutia slides into the position of the 2nd. */
- }
- }
- /* Return successfully. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: dump_minutiae - Given a minutiae list, writes a formatted text report of
- #cat: the list's contents to the specified open file pointer.
- Input:
- minutiae - list of minutia structures
- Output:
- fpout - open file pointer
- **************************************************************************/
- void dump_minutiae(FILE *fpout, const MINUTIAE *minutiae)
- {
- int i, j;
- fprintf(fpout, "\n%d Minutiae Detected\n\n", minutiae->num);
- for(i = 0; i < minutiae->num; i++){
- /* Precision of reliablity added one decimal position */
- /* on 09-13-04 */
- fprintf(fpout, "%4d : %4d, %4d : %2d : %6.3f :", i,
- minutiae->list[i]->x, minutiae->list[i]->y,
- minutiae->list[i]->direction, minutiae->list[i]->reliability);
- if(minutiae->list[i]->type == RIDGE_ENDING)
- fprintf(fpout, "RIG : ");
- else
- fprintf(fpout, "BIF : ");
-
- if(minutiae->list[i]->appearing)
- fprintf(fpout, "APP : ");
- else
- fprintf(fpout, "DIS : ");
- fprintf(fpout, "%2d ", minutiae->list[i]->feature_id);
- for(j = 0; j < minutiae->list[i]->num_nbrs; j++){
- fprintf(fpout, ": %4d,%4d; %2d ",
- minutiae->list[minutiae->list[i]->nbrs[j]]->x,
- minutiae->list[minutiae->list[i]->nbrs[j]]->y,
- minutiae->list[i]->ridge_counts[j]);
- }
- fprintf(fpout, "\n");
- }
- }
- /*************************************************************************
- **************************************************************************
- #cat: dump_minutiae_pts - Given a minutiae list, writes the coordinate point
- #cat: for each minutia in the list to the specified open
- #cat: file pointer.
- Input:
- minutiae - list of minutia structures
- Output:
- fpout - open file pointer
- **************************************************************************/
- void dump_minutiae_pts(FILE *fpout, const MINUTIAE *minutiae)
- {
- int i;
- /* First line in the output file contians the number of minutia */
- /* points to be written to the file. */
- fprintf(fpout, "%d\n", minutiae->num);
- /* Foreach minutia in list... */
- for(i = 0; i < minutiae->num; i++){
- /* Write the minutia's coordinate point to the file pointer. */
- fprintf(fpout, "%4d %4d\n", minutiae->list[i]->x, minutiae->list[i]->y);
- }
- }
- /*************************************************************************
- **************************************************************************
- #cat: dump_reliable_minutiae_pts - Given a minutiae list, writes the
- #cat: coordinate point for each minutia in the list that has
- #cat: the specified reliability to the specified open
- #cat: file pointer.
- Input:
- minutiae - list of minutia structures
- reliability - desired reliability level for minutiae to be reported
- Output:
- fpout - open file pointer
- **************************************************************************/
- void dump_reliable_minutiae_pts(FILE *fpout, const MINUTIAE *minutiae,
- const double reliability)
- {
- int i, count;
- /* First count the number of qualifying minutiae so that the */
- /* MFS header may be written. */
- count = 0;
- /* Foreach minutia in list... */
- for(i = 0; i < minutiae->num; i++){
- if(minutiae->list[i]->reliability == reliability)
- count++;
- }
- /* First line in the output file contians the number of minutia */
- /* points to be written to the file. */
- fprintf(fpout, "%d\n", count);
- /* Foreach minutia in list... */
- for(i = 0; i < minutiae->num; i++){
- if(minutiae->list[i]->reliability == reliability)
- /* Write the minutia's coordinate point to the file pointer. */
- fprintf(fpout, "%4d %4d\n",
- minutiae->list[i]->x, minutiae->list[i]->y);
- }
- }
- /*************************************************************************
- **************************************************************************
- #cat: create_minutia - Takes attributes associated with a detected minutia
- #cat: point and allocates and initializes a minutia structure.
- Input:
- x_loc - x-pixel coord of minutia (interior to feature)
- y_loc - y-pixel coord of minutia (interior to feature)
- x_edge - x-pixel coord of corresponding edge pixel (exterior to feature)
- y_edge - y-pixel coord of corresponding edge pixel (exterior to feature)
- idir - integer direction of the minutia
- reliability - floating point measure of minutia's reliability
- type - type of the minutia (ridge-ending or bifurcation)
- appearing - designates the minutia as appearing or disappearing
- feature_id - index of minutia's matching feature_patterns[]
- Output:
- ominutia - ponter to an allocated and initialized minutia structure
- Return Code:
- Zero - minutia structure successfully allocated and initialized
- Negative - system error
- *************************************************************************/
- int create_minutia(MINUTIA **ominutia, const int x_loc, const int y_loc,
- const int x_edge, const int y_edge, const int idir,
- const double reliability,
- const int type, const int appearing, const int feature_id)
- {
- MINUTIA *minutia;
- /* Allocate a minutia structure. */
- minutia = (MINUTIA *)malloc(sizeof(MINUTIA));
- /* If allocation error... */
- if(minutia == (MINUTIA *)NULL){
- fprintf(stderr, "ERROR : create_minutia : malloc : minutia\n");
- return(-230);
- }
- /* Assign minutia structure attributes. */
- minutia->x = x_loc;
- minutia->y = y_loc;
- minutia->ex = x_edge;
- minutia->ey = y_edge;
- minutia->direction = idir;
- minutia->reliability = reliability;
- minutia->type = type;
- minutia->appearing = appearing;
- minutia->feature_id = feature_id;
- minutia->nbrs = (int *)NULL;
- minutia->ridge_counts = (int *)NULL;
- minutia->num_nbrs = 0;
- /* Set minutia object to output pointer. */
- *ominutia = minutia;
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: free_minutiae - Takes a minutiae list and deallocates all memory
- #cat: associated with it.
- Input:
- minutiae - pointer to allocated list of minutia structures
- *************************************************************************/
- void free_minutiae(MINUTIAE *minutiae)
- {
- int i;
- /* Deallocate minutia structures in the list. */
- for(i = 0; i < minutiae->num; i++)
- free_minutia(minutiae->list[i]);
- /* Deallocate list of minutia pointers. */
- free(minutiae->list);
- /* Deallocate the list structure. */
- free(minutiae);
- }
- /*************************************************************************
- **************************************************************************
- #cat: free_minutia - Takes a minutia pointer and deallocates all memory
- #cat: associated with it.
- Input:
- minutia - pointer to allocated minutia structure
- *************************************************************************/
- void free_minutia(MINUTIA *minutia)
- {
- /* Deallocate sublists. */
- if(minutia->nbrs != (int *)NULL)
- free(minutia->nbrs);
- if(minutia->ridge_counts != (int *)NULL)
- free(minutia->ridge_counts);
- /* Deallocate the minutia structure. */
- free(minutia);
- }
- /*************************************************************************
- **************************************************************************
- #cat: remove_minutia - Removes the specified minutia point from the input
- #cat: list of minutiae.
- Input:
- index - position of minutia to be removed from list
- minutiae - input list of minutiae
- Output:
- minutiae - list with minutia removed
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int remove_minutia(const int index, MINUTIAE *minutiae)
- {
- int fr, to;
- /* Make sure the requested index is within range. */
- if((index < 0) && (index >= minutiae->num)){
- fprintf(stderr, "ERROR : remove_minutia : index out of range\n");
- return(-380);
- }
- /* Deallocate the minutia structure to be removed. */
- free_minutia(minutiae->list[index]);
- /* Slide the remaining list of minutiae up over top of the */
- /* position of the minutia being removed. */
- for(to = index, fr = index+1; fr < minutiae->num; to++, fr++)
- minutiae->list[to] = minutiae->list[fr];
- /* Decrement the number of minutiae remaining in the list. */
- minutiae->num--;
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: join_minutia - Takes 2 minutia points and connectes their features in
- #cat: the input binary image. A line is drawn in the image
- #cat: between the 2 minutia with a specified line-width radius
- #cat: and a conditional border of pixels opposite in color
- #cat: from the interior line.
- Input:
- minutia1 - first minutia point to be joined
- minutia2 - second minutia point to be joined
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- with_boundary - signifies the inclusion of border pixels
- line_radius - line-width radius of join line
- Output:
- bdata - edited image with minutia features joined
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int join_minutia(const MINUTIA *minutia1, const MINUTIA *minutia2,
- unsigned char *bdata, const int iw, const int ih,
- const int with_boundary, const int line_radius)
- {
- int dx_gte_dy, delta_x, delta_y;
- int *x_list, *y_list, num;
- int minutia_pix, boundary_pix;
- int i, j, ret;
- int x1, y1, x2, y2;
- /* Compute X and Y deltas between minutia points. */
- delta_x = abs(minutia1->x - minutia2->x);
- delta_y = abs(minutia1->y - minutia2->y);
- /* Set flag based on |DX| >= |DY|. */
- /* If flag is true then add additional pixel width to the join line */
- /* by adding pixels neighboring top and bottom. */
- /* If flag is false then add additional pixel width to the join line */
- /* by adding pixels neighboring left and right. */
- if(delta_x >= delta_y)
- dx_gte_dy = 1;
- else
- dx_gte_dy = 0;
- /* Compute points along line segment between the two minutia points. */
- if((ret = line_points(&x_list, &y_list, &num,
- minutia1->x, minutia1->y, minutia2->x, minutia2->y)))
- /* If error with line routine, return error code. */
- return(ret);
- /* Determine pixel color of minutia and boundary. */
- if(minutia1->type == RIDGE_ENDING){
- /* To connect 2 ridge-endings, draw black. */
- minutia_pix = 1;
- boundary_pix = 0;
- }
- else{
- /* To connect 2 bifurcations, draw white. */
- minutia_pix = 0;
- boundary_pix = 1;
- }
- /* Foreach point on line connecting the minutiae points ... */
- for(i = 1; i < num-1; i++){
- /* Draw minutia pixel at current point on line. */
- *(bdata+(y_list[i]*iw)+x_list[i]) = minutia_pix;
- /* Initialize starting corrdinates for adding width to the */
- /* join line to the current point on the line. */
- x1 = x_list[i];
- y1 = y_list[i];
- x2 = x1;
- y2 = y1;
- /* Foreach pixel of added radial width ... */
- for(j = 0; j < line_radius; j++){
- /* If |DX|>=|DY|, we want to add width to line by writing */
- /* to pixels neighboring above and below. */
- /* x1 -= (0=(1-1)); y1 -= 1 ==> ABOVE */
- /* x2 += (0=(1-1)); y2 += 1 ==> BELOW */
- /* If |DX|<|DY|, we want to add width to line by writing */
- /* to pixels neighboring left and right. */
- /* x1 -= (1=(1-0)); y1 -= 0 ==> LEFT */
- /* x2 += (1=(1-0)); y2 += 0 ==> RIGHT */
- /* Advance 1st point along width dimension. */
- x1 -= (1 - dx_gte_dy);
- y1 -= dx_gte_dy;
- /* If pixel 1st point is within image boundaries ... */
- if((x1 >= 0) && (x1 < iw) &&
- (y1 >= 0) && (y1 < ih))
- /* Write the pixel ABOVE or LEFT. */
- *(bdata+(y1*iw)+x1) = minutia_pix;
- /* Advance 2nd point along width dimension. */
- x2 += (1 - dx_gte_dy);
- y2 += dx_gte_dy;
- /* If pixel 2nd point is within image boundaries ... */
- if((x2 >= 0) && (x2 < iw) &&
- /* Write the pixel BELOW or RIGHT. */
- (y2 >= 0) && (y2 < ih))
- *(bdata+(y2*iw)+x2) = minutia_pix;
- }
- /* If boundary flag is set ... draw the boundary pixels.*/
- if(with_boundary){
- /* Advance 1st point along width dimension. */
- x1 -= (1 - dx_gte_dy);
- y1 -= dx_gte_dy;
- /* If pixel 1st point is within image boundaries ... */
- if((x1 >= 0) && (x1 < iw) &&
- (y1 >= 0) && (y1 < ih))
- /* Write the pixel ABOVE or LEFT of opposite color. */
- *(bdata+(y1*iw)+x1) = boundary_pix;
- /* Advance 2nd point along width dimension. */
- x2 += (1 - dx_gte_dy);
- y2 += dx_gte_dy;
- /* If pixel 2nd point is within image boundaries ... */
- if((x2 >= 0) && (x2 < iw) &&
- (y2 >= 0) && (y2 < ih))
- /* Write the pixel BELOW or RIGHT of opposite color. */
- *(bdata+(y2*iw)+x2) = boundary_pix;
- }
- }
- /* Deallocate points along connecting line. */
- free(x_list);
- free(y_list);
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: minutia_type - Given the pixel color of the detected feature, returns
- #cat: whether the minutia is a ridge-ending (black pixel) or
- #cat: bifurcation (white pixel).
- Input:
- feature_pix - pixel color of the feature's interior
- Return Code:
- RIDGE_ENDING - minutia is a ridge-ending
- BIFURCATION - minutia is a bifurcation (valley-ending)
- **************************************************************************/
- int minutia_type(const int feature_pix)
- {
- int type;
- /* If feature pixel is white ... */
- if(feature_pix == 0)
- /* Then the feature is a valley-ending, so BIFURCATION. */
- type = BIFURCATION;
- /* Otherwise, the feature pixel is black ... */
- else
- /* So the feature is a RIDGE-ENDING. */
- type = RIDGE_ENDING;
- /* Return the type. */
- return(type);
- }
- /*************************************************************************
- **************************************************************************
- #cat: is_minutia_appearing - Given the pixel location of a minutia feature
- #cat: and its corresponding adjacent edge pixel, returns whether
- #cat: the minutia is appearing or disappearing. Remeber, that
- #cat: "feature" refers to either a ridge or valley-ending.
- Input:
- x_loc - x-pixel coord of feature (interior to feature)
- y_loc - y-pixel coord of feature (interior to feature)
- x_edge - x-pixel coord of corresponding edge pixel
- (exterior to feature)
- y_edge - y-pixel coord of corresponding edge pixel
- (exterior to feature)
- Return Code:
- APPEARING - minutia is appearing (TRUE==1)
- DISAPPEARING - minutia is disappearing (FALSE==0)
- Negative - system error
- **************************************************************************/
- int is_minutia_appearing(const int x_loc, const int y_loc,
- const int x_edge, const int y_edge)
- {
- /* Edge pixels will always be N,S,E,W of feature pixel. */
- /* 1. When scanning for feature's HORIZONTALLY... */
- /* If the edge is above the feature, then appearing. */
- if(x_edge < x_loc)
- return(APPEARING);
- /* If the edge is below the feature, then disappearing. */
- if(x_edge > x_loc)
- return(DISAPPEARING);
- /* 1. When scanning for feature's VERTICALLY... */
- /* If the edge is left of feature, then appearing. */
- if(y_edge < y_loc)
- return(APPEARING);
- /* If the edge is right of feature, then disappearing. */
- if(y_edge > y_loc)
- return(DISAPPEARING);
- /* Should never get here, but just in case. */
- fprintf(stderr,
- "ERROR : is_minutia_appearing : bad configuration of pixels\n");
- return(-240);
- }
- /*************************************************************************
- **************************************************************************
- #cat: choose_scan_direction - Determines the orientation (horizontal or
- #cat: vertical) in which a block is to be scanned for minutiae.
- #cat: The orientation is based on the blocks corresponding IMAP
- #cat: direction.
- Input:
- imapval - Block's IMAP direction
- ndirs - number of possible IMAP directions (within semicircle)
- Return Code:
- SCAN_HORIZONTAL - horizontal orientation
- SCAN_VERTICAL - vertical orientation
- **************************************************************************/
- int choose_scan_direction(const int imapval, const int ndirs)
- {
- int qtr_ndirs;
- /* Compute quarter of directions in semi-circle. */
- qtr_ndirs = ndirs>>2;
- /* If ridge flow in block is relatively vertical, then we want */
- /* to scan for minutia features in the opposite direction */
- /* (ie. HORIZONTALLY). */
- if((imapval <= qtr_ndirs) || (imapval > (qtr_ndirs*3)))
- return(SCAN_HORIZONTAL);
- /* Otherwise, ridge flow is realtively horizontal, and we want */
- /* to scan for minutia features in the opposite direction */
- /* (ie. VERTICALLY). */
- else
- return(SCAN_VERTICAL);
- }
- /*************************************************************************
- **************************************************************************
- #cat: scan4minutiae - Scans a block of binary image data detecting potential
- #cat: minutiae points.
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- blk_x - x-block coord to be scanned
- blk_y - y-block coord to be scanned
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- scan_x - x-pixel coord of origin of region to be scanned
- scan_y - y-pixel coord of origin of region to be scanned
- scan_w - width (in pixels) of region to be scanned
- scan_h - height (in pixels) of region to be scanned
- scan_dir - the scan orientation (horizontal or vertical)
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int scan4minutiae(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap,
- const int blk_x, const int blk_y, const int mw, const int mh,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h, const int scan_dir,
- const LFSPARMS *lfsparms)
- {
- int blk_i, ret;
- /* Compute block index from block coordinates. */
- blk_i = (blk_y*mw) + blk_x;
- /* Conduct primary scan for minutiae horizontally. */
- if(scan_dir == SCAN_HORIZONTAL){
- if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- scan_x, scan_y, scan_w, scan_h, lfsparms))){
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- }
- /* Rescan block vertically. */
- if((ret = rescan4minutiae_vertically(minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms))){
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- }
- }
- /* Otherwise, conduct primary scan for minutiae vertically. */
- else{
- if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- scan_x, scan_y, scan_w, scan_h, lfsparms))){
- /* Return resulting code. */
- return(ret);
- }
- /* Rescan block horizontally. */
- if((ret = rescan4minutiae_horizontally(minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms))){
- /* Return resulting code. */
- return(ret);
- }
- }
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: scan4minutiae_horizontally - Scans a specified region of binary image
- #cat: data horizontally, detecting potential minutiae points.
- #cat: Minutia detected via the horizontal scan process are
- #cat: by nature vertically oriented (orthogonal to the scan).
- #cat: The region actually scanned is slightly larger than that
- #cat: specified. This overlap attempts to minimize the number
- #cat: of minutiae missed at the region boundaries.
- #cat: HOWEVER, some minutiae will still be missed!
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imapval - IMAP value associated with this image region
- nmapval - NMAP value associated with this image region
- scan_x - x-pixel coord of origin of region to be scanned
- scan_y - y-pixel coord of origin of region to be scanned
- scan_w - width (in pixels) of region to be scanned
- scan_h - height (in pixels) of region to be scanned
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int scan4minutiae_horizontally(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int imapval, const int nmapval,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int sx, sy, ex, ey, cx, cy, x2;
- unsigned char *p1ptr, *p2ptr;
- int possible[NFEATURES], nposs;
- int ret;
- /* NOTE!!! Minutia that "straddle" region boundaries may be missed! */
- /* If possible, overlap left and right of current scan region */
- /* by 2 pixel columns to help catch some minutia that straddle the */
- /* the scan region boundaries. */
- sx = max(0, scan_x-2);
- ex = min(iw, scan_x+scan_w+2);
- /* If possible, overlap the scan region below by 1 pixel row. */
- sy = scan_y;
- ey = min(ih, scan_y+scan_h+1);
- /* For now, we will not adjust for IMAP edge, as the binary image */
- /* was properly padded at its edges so as not to cause anomallies. */
- /* Start at first row in region. */
- cy = sy;
- /* While second scan row not outside the bottom of the scan region... */
- while(cy+1 < ey){
- /* Start at beginning of new scan row in region. */
- cx = sx;
- /* While not at end of region's current scan row. */
- while(cx < ex){
- /* Get pixel pair from current x position in current and next */
- /* scan rows. */
- p1ptr = bdata+(cy*iw)+cx;
- p2ptr = bdata+((cy+1)*iw)+cx;
- /* If scan pixel pair matches first pixel pair of */
- /* 1 or more features... */
- if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Bump forward to next scan pixel pair. */
- cx++;
- p1ptr++;
- p2ptr++;
- /* If not at end of region's current scan row... */
- if(cx < ex){
- /* If scan pixel pair matches second pixel pair of */
- /* 1 or more features... */
- if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Store current x location. */
- x2 = cx;
- /* Skip repeated pixel pairs. */
- skip_repeated_horizontal_pair(&cx, ex, &p1ptr, &p2ptr,
- iw, ih);
- /* If not at end of region's current scan row... */
- if(cx < ex){
- /* If scan pixel pair matches third pixel pair of */
- /* a single feature... */
- if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Process detected minutia point. */
- if((ret = process_horizontal_scan_minutia(minutiae,
- cx, cy, x2, possible[0],
- bdata, iw, ih,
- imapval, nmapval, lfsparms))){
- /* Return code may be: */
- /* 1. ret< 0 (implying system error) */
- /* 2. ret==IGNORE (ignore current feature) */
- if(ret < 0)
- return(ret);
- /* Otherwise, IGNORE and continue. */
- }
- }
- /* Set up to resume scan. */
- /* Test to see if 3rd pair can slide into 2nd pair. */
- /* The values of the 2nd pair MUST be different. */
- /* If 3rd pair values are different ... */
- if(*p1ptr != *p2ptr){
- /* Set next first pair to last of repeated */
- /* 2nd pairs, ie. back up one pair. */
- cx--;
- }
- /* Otherwise, 3rd pair can't be a 2nd pair, so */
- /* keep pointing to 3rd pair so that it is used */
- /* in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 2nd pair failed, so keep pointing to it */
- /* so that it is used in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 1st pair failed... */
- else{
- /* Bump forward to next pixel pair. */
- cx++;
- }
- } /* While not at end of current scan row. */
- /* Bump forward to next scan row. */
- cy++;
- } /* While not out of scan rows. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: scan4minutiae_horizontally_V2 - Scans an entire binary image
- #cat: horizontally, detecting potential minutiae points.
- #cat: Minutia detected via the horizontal scan process are
- #cat: by nature vertically oriented (orthogonal to the scan).
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- pdirection_map - pixelized Direction Map
- plow_flow_map - pixelized Low Ridge Flow Map
- phigh_curve_map - pixelized High Curvature Map
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int scan4minutiae_horizontally_V2(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
- const LFSPARMS *lfsparms)
- {
- int sx, sy, ex, ey, cx, cy, x2;
- unsigned char *p1ptr, *p2ptr;
- int possible[NFEATURES], nposs;
- int ret;
- /* Set scan region to entire image. */
- sx = 0;
- ex = iw;
- sy = 0;
- ey = ih;
- /* Start at first row in region. */
- cy = sy;
- /* While second scan row not outside the bottom of the scan region... */
- while(cy+1 < ey){
- /* Start at beginning of new scan row in region. */
- cx = sx;
- /* While not at end of region's current scan row. */
- while(cx < ex){
- /* Get pixel pair from current x position in current and next */
- /* scan rows. */
- p1ptr = bdata+(cy*iw)+cx;
- p2ptr = bdata+((cy+1)*iw)+cx;
- /* If scan pixel pair matches first pixel pair of */
- /* 1 or more features... */
- if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Bump forward to next scan pixel pair. */
- cx++;
- p1ptr++;
- p2ptr++;
- /* If not at end of region's current scan row... */
- if(cx < ex){
- /* If scan pixel pair matches second pixel pair of */
- /* 1 or more features... */
- if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Store current x location. */
- x2 = cx;
- /* Skip repeated pixel pairs. */
- skip_repeated_horizontal_pair(&cx, ex, &p1ptr, &p2ptr,
- iw, ih);
- /* If not at end of region's current scan row... */
- if(cx < ex){
- /* If scan pixel pair matches third pixel pair of */
- /* a single feature... */
- if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Process detected minutia point. */
- if((ret = process_horizontal_scan_minutia_V2(minutiae,
- cx, cy, x2, possible[0],
- bdata, iw, ih, pdirection_map,
- plow_flow_map, phigh_curve_map,
- lfsparms))){
- /* Return code may be: */
- /* 1. ret< 0 (implying system error) */
- /* 2. ret==IGNORE (ignore current feature) */
- if(ret < 0)
- return(ret);
- /* Otherwise, IGNORE and continue. */
- }
- }
- /* Set up to resume scan. */
- /* Test to see if 3rd pair can slide into 2nd pair. */
- /* The values of the 2nd pair MUST be different. */
- /* If 3rd pair values are different ... */
- if(*p1ptr != *p2ptr){
- /* Set next first pair to last of repeated */
- /* 2nd pairs, ie. back up one pair. */
- cx--;
- }
- /* Otherwise, 3rd pair can't be a 2nd pair, so */
- /* keep pointing to 3rd pair so that it is used */
- /* in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 2nd pair failed, so keep pointing to it */
- /* so that it is used in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 1st pair failed... */
- else{
- /* Bump forward to next pixel pair. */
- cx++;
- }
- } /* While not at end of current scan row. */
- /* Bump forward to next scan row. */
- cy++;
- } /* While not out of scan rows. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: scan4minutiae_vertically - Scans a specified region of binary image data
- #cat: vertically, detecting potential minutiae points.
- #cat: Minutia detected via the vetical scan process are
- #cat: by nature horizontally oriented (orthogonal to the scan).
- #cat: The region actually scanned is slightly larger than that
- #cat: specified. This overlap attempts to minimize the number
- #cat: of minutiae missed at the region boundaries.
- #cat: HOWEVER, some minutiae will still be missed!
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imapval - IMAP value associated with this image region
- nmapval - NMAP value associated with this image region
- scan_x - x-pixel coord of origin of region to be scanned
- scan_y - y-pixel coord of origin of region to be scanned
- scan_w - width (in pixels) of region to be scanned
- scan_h - height (in pixels) of region to be scanned
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int scan4minutiae_vertically(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int imapval, const int nmapval,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int sx, sy, ex, ey, cx, cy, y2;
- unsigned char *p1ptr, *p2ptr;
- int possible[NFEATURES], nposs;
- int ret;
- /* NOTE!!! Minutia that "straddle" region boundaries may be missed! */
- /* If possible, overlap scan region to the right by 1 pixel column. */
- sx = scan_x;
- ex = min(iw, scan_x+scan_w+1);
- /* If possible, overlap top and bottom of current scan region */
- /* by 2 pixel rows to help catch some minutia that straddle the */
- /* the scan region boundaries. */
- sy = max(0, scan_y-2);
- ey = min(ih, scan_y+scan_h+2);
- /* For now, we will not adjust for IMAP edge, as the binary image */
- /* was properly padded at its edges so as not to cause anomalies. */
- /* Start at first column in region. */
- cx = sx;
- /* While second scan column not outside the right of the region ... */
- while(cx+1 < ex){
- /* Start at beginning of new scan column in region. */
- cy = sy;
- /* While not at end of region's current scan column. */
- while(cy < ey){
- /* Get pixel pair from current y position in current and next */
- /* scan columns. */
- p1ptr = bdata+(cy*iw)+cx;
- p2ptr = p1ptr+1;
- /* If scan pixel pair matches first pixel pair of */
- /* 1 or more features... */
- if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Bump forward to next scan pixel pair. */
- cy++;
- p1ptr+=iw;
- p2ptr+=iw;
- /* If not at end of region's current scan column... */
- if(cy < ey){
- /* If scan pixel pair matches second pixel pair of */
- /* 1 or more features... */
- if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Store current y location. */
- y2 = cy;
- /* Skip repeated pixel pairs. */
- skip_repeated_vertical_pair(&cy, ey, &p1ptr, &p2ptr,
- iw, ih);
- /* If not at end of region's current scan column... */
- if(cy < ey){
- /* If scan pixel pair matches third pixel pair of */
- /* a single feature... */
- if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Process detected minutia point. */
- if((ret = process_vertical_scan_minutia(minutiae,
- cx, cy, y2, possible[0],
- bdata, iw, ih,
- imapval, nmapval, lfsparms))){
- /* Return code may be: */
- /* 1. ret< 0 (implying system error) */
- /* 2. ret==IGNORE (ignore current feature) */
- if(ret < 0)
- return(ret);
- /* Otherwise, IGNORE and continue. */
- }
- }
- /* Set up to resume scan. */
- /* Test to see if 3rd pair can slide into 2nd pair. */
- /* The values of the 2nd pair MUST be different. */
- /* If 3rd pair values are different ... */
- if(*p1ptr != *p2ptr){
- /* Set next first pair to last of repeated */
- /* 2nd pairs, ie. back up one pair. */
- cy--;
- }
- /* Otherwise, 3rd pair can't be a 2nd pair, so */
- /* keep pointing to 3rd pair so that it is used */
- /* in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 2nd pair failed, so keep pointing to it */
- /* so that it is used in the next first pair test. */
- } /* Else, at end of current scan column. */
- }
- /* Otherwise, 1st pair failed... */
- else{
- /* Bump forward to next pixel pair. */
- cy++;
- }
- } /* While not at end of current scan column. */
- /* Bump forward to next scan column. */
- cx++;
- } /* While not out of scan columns. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: scan4minutiae_vertically_V2 - Scans an entire binary image
- #cat: vertically, detecting potential minutiae points.
- #cat: Minutia detected via the vetical scan process are
- #cat: by nature horizontally oriented (orthogonal to the scan).
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- pdirection_map - pixelized Direction Map
- plow_flow_map - pixelized Low Ridge Flow Map
- phigh_curve_map - pixelized High Curvature Map
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int scan4minutiae_vertically_V2(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
- const LFSPARMS *lfsparms)
- {
- int sx, sy, ex, ey, cx, cy, y2;
- unsigned char *p1ptr, *p2ptr;
- int possible[NFEATURES], nposs;
- int ret;
- /* Set scan region to entire image. */
- sx = 0;
- ex = iw;
- sy = 0;
- ey = ih;
- /* Start at first column in region. */
- cx = sx;
- /* While second scan column not outside the right of the region ... */
- while(cx+1 < ex){
- /* Start at beginning of new scan column in region. */
- cy = sy;
- /* While not at end of region's current scan column. */
- while(cy < ey){
- /* Get pixel pair from current y position in current and next */
- /* scan columns. */
- p1ptr = bdata+(cy*iw)+cx;
- p2ptr = p1ptr+1;
- /* If scan pixel pair matches first pixel pair of */
- /* 1 or more features... */
- if(match_1st_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Bump forward to next scan pixel pair. */
- cy++;
- p1ptr+=iw;
- p2ptr+=iw;
- /* If not at end of region's current scan column... */
- if(cy < ey){
- /* If scan pixel pair matches second pixel pair of */
- /* 1 or more features... */
- if(match_2nd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Store current y location. */
- y2 = cy;
- /* Skip repeated pixel pairs. */
- skip_repeated_vertical_pair(&cy, ey, &p1ptr, &p2ptr,
- iw, ih);
- /* If not at end of region's current scan column... */
- if(cy < ey){
- /* If scan pixel pair matches third pixel pair of */
- /* a single feature... */
- if(match_3rd_pair(*p1ptr, *p2ptr, possible, &nposs)){
- /* Process detected minutia point. */
- if((ret = process_vertical_scan_minutia_V2(minutiae,
- cx, cy, y2, possible[0],
- bdata, iw, ih, pdirection_map,
- plow_flow_map, phigh_curve_map,
- lfsparms))){
- /* Return code may be: */
- /* 1. ret< 0 (implying system error) */
- /* 2. ret==IGNORE (ignore current feature) */
- if(ret < 0)
- return(ret);
- /* Otherwise, IGNORE and continue. */
- }
- }
- /* Set up to resume scan. */
- /* Test to see if 3rd pair can slide into 2nd pair. */
- /* The values of the 2nd pair MUST be different. */
- /* If 3rd pair values are different ... */
- if(*p1ptr != *p2ptr){
- /* Set next first pair to last of repeated */
- /* 2nd pairs, ie. back up one pair. */
- cy--;
- }
- /* Otherwise, 3rd pair can't be a 2nd pair, so */
- /* keep pointing to 3rd pair so that it is used */
- /* in the next first pair test. */
- } /* Else, at end of current scan row. */
- }
- /* Otherwise, 2nd pair failed, so keep pointing to it */
- /* so that it is used in the next first pair test. */
- } /* Else, at end of current scan column. */
- }
- /* Otherwise, 1st pair failed... */
- else{
- /* Bump forward to next pixel pair. */
- cy++;
- }
- } /* While not at end of current scan column. */
- /* Bump forward to next scan column. */
- cx++;
- } /* While not out of scan columns. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: rescan4minutiae_horizontally - Rescans portions of a block of binary
- #cat: image data horizontally for potential minutiae. The areas
- #cat: rescanned within the block are based on the current
- #cat: block's neighboring blocks' IMAP and NMAP values.
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- blk_x - x-block coord to be rescanned
- blk_y - y-block coord to be rescanned
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- scan_x - x-pixel coord of origin of region to be rescanned
- scan_y - y-pixel coord of origin of region to be rescanned
- scan_w - width (in pixels) of region to be rescanned
- scan_h - height (in pixels) of region to be rescanned
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int rescan4minutiae_horizontally(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap,
- const int blk_x, const int blk_y,
- const int mw, const int mh,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int blk_i, ret;
- /* Compute block index from block coordinates. */
- blk_i = (blk_y*mw)+blk_x;
- /* If high-curve block... */
- if(nmap[blk_i] == HIGH_CURVATURE){
- /* Rescan entire block in orthogonal direction. */
- if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- }
- /* Otherwise, block is low-curvature. */
- else{
- /* 1. Rescan horizontally to the North. */
- if((ret = rescan_partial_horizontally(NORTH, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- /* 2. Rescan horizontally to the East. */
- if((ret = rescan_partial_horizontally(EAST, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- /* 3. Rescan horizontally to the South. */
- if((ret = rescan_partial_horizontally(SOUTH, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- /* 4. Rescan horizontally to the West. */
- if((ret = rescan_partial_horizontally(WEST, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- } /* End low-curvature rescan. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: rescan4minutiae_vertically - Rescans portions of a block of binary
- #cat: image data vertically for potential minutiae. The areas
- #cat: rescanned within the block are based on the current
- #cat: block's neighboring blocks' IMAP and NMAP values.
- Input:
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- blk_x - x-block coord to be rescanned
- blk_y - y-block coord to be rescanned
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- scan_x - x-pixel coord of origin of region to be rescanned
- scan_y - y-pixel coord of origin of region to be rescanned
- scan_w - width (in pixels) of region to be rescanned
- scan_h - height (in pixels) of region to be rescanned
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int rescan4minutiae_vertically(MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap,
- const int blk_x, const int blk_y,
- const int mw, const int mh,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int blk_i, ret;
- /* Compute block index from block coordinates. */
- blk_i = (blk_y*mw)+blk_x;
- /* If high-curve block... */
- if(nmap[blk_i] == HIGH_CURVATURE){
- /* Rescan entire block in orthogonal direction. */
- if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- }
- /* Otherwise, block is low-curvature. */
- else{
- /* 1. Rescan vertically to the North. */
- if((ret = rescan_partial_vertically(NORTH, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- /* 2. Rescan vertically to the East. */
- if((ret = rescan_partial_vertically(EAST, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- /* 3. Rescan vertically to the South. */
- if((ret = rescan_partial_vertically(SOUTH, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- /* 4. Rescan vertically to the West. */
- if((ret = rescan_partial_vertically(WEST, minutiae, bdata, iw, ih,
- imap, nmap, blk_x, blk_y, mw, mh,
- scan_x, scan_y, scan_w, scan_h, lfsparms)))
- return(ret);
- } /* End low-curvature rescan. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: rescan_partial_horizontally - Rescans a portion of a block of binary
- #cat: image data horizontally based on the IMAP and NMAP values
- #cat: of a specified neighboring block.
- Input:
- nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- blk_x - x-block coord to be rescanned
- blk_y - y-block coord to be rescanned
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- scan_x - x-pixel coord of origin of image region
- scan_y - y-pixel coord of origin of image region
- scan_w - width (in pixels) of image region
- scan_h - height (in pixels) of image region
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int rescan_partial_horizontally(const int nbr_dir, MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap,
- const int blk_x, const int blk_y,
- const int mw, const int mh,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int nblk_i, blk_i;
- int rescan_dir;
- int rescan_x, rescan_y, rescan_w, rescan_h;
- int ret;
- /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
- ret = get_nbr_block_index(&nblk_i, nbr_dir, blk_x, blk_y, mw, mh);
- /* Will return: */
- /* 1. Neighbor index found == FOUND */
- /* 2. Neighbor not found == NOT_FOUND */
- /* 3. System error < 0 */
- /* If system error ... */
- if(ret < 0)
- /* Return the error code. */
- return(ret);
- /* If neighbor not found ... */
- if(ret == NOT_FOUND)
- /* Nothing to do, so return normally. */
- return(0);
- /* Otherwise, neighboring block found ... */
- /* If neighbor block is VALID... */
- if(imap[nblk_i] != INVALID_DIR){
- /* Compute block index from current (not neighbor) block coordinates. */
- blk_i = (blk_y*mw)+blk_x;
- /* Select feature scan direction based on neighbor IMAP. */
- rescan_dir = choose_scan_direction(imap[nblk_i],
- lfsparms->num_directions);
- /* If new scan direction is HORIZONTAL... */
- if(rescan_dir == SCAN_HORIZONTAL){
- /* Adjust scan_x, scan_y, scan_w, scan_h for rescan. */
- if((ret = adjust_horizontal_rescan(nbr_dir, &rescan_x, &rescan_y,
- &rescan_w, &rescan_h,
- scan_x, scan_y, scan_w, scan_h, lfsparms->blocksize)))
- /* Return system error code. */
- return(ret);
- /* Rescan specified region in block vertically. */
- /* Pass IMAP direction for the block, NOT its neighbor. */
- if((ret = scan4minutiae_horizontally(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- rescan_x, rescan_y, rescan_w, rescan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- } /* Otherwise, block has already been scanned vertically. */
- } /* Otherwise, neighbor has INVALID IMAP, so ignore rescan. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: rescan_partial_vertically - Rescans a portion of a block of binary
- #cat: image data vertically based on the IMAP and NMAP values
- #cat: of a specified neighboring block.
- Input:
- nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imap - matrix of ridge flow directions
- nmap - IMAP augmented with blocks of HIGH-CURVATURE and
- blocks which have no neighboring valid directions.
- blk_x - x-block coord to be rescanned
- blk_y - y-block coord to be rescanned
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- scan_x - x-pixel coord of origin of image region
- scan_y - y-pixel coord of origin of image region
- scan_w - width (in pixels) of image region
- scan_h - height (in pixels) of image region
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int rescan_partial_vertically(const int nbr_dir, MINUTIAE *minutiae,
- unsigned char *bdata, const int iw, const int ih,
- const int *imap, const int *nmap,
- const int blk_x, const int blk_y,
- const int mw, const int mh,
- const int scan_x, const int scan_y,
- const int scan_w, const int scan_h,
- const LFSPARMS *lfsparms)
- {
- int nblk_i, blk_i;
- int rescan_dir;
- int rescan_x, rescan_y, rescan_w, rescan_h;
- int ret;
- /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
- ret = get_nbr_block_index(&nblk_i, nbr_dir, blk_x, blk_y, mw, mh);
- /* Will return: */
- /* 1. Neighbor index found == FOUND */
- /* 2. Neighbor not found == NOT_FOUND */
- /* 3. System error < 0 */
- /* If system error ... */
- if(ret < 0)
- /* Return the error code. */
- return(ret);
- /* If neighbor not found ... */
- if(ret == NOT_FOUND)
- /* Nothing to do, so return normally. */
- return(0);
- /* Otherwise, neighboring block found ... */
- /* If neighbor block is VALID... */
- if(imap[nblk_i] != INVALID_DIR){
- /* Compute block index from current (not neighbor) block coordinates. */
- blk_i = (blk_y*mw)+blk_x;
- /* Select feature scan direction based on neighbor IMAP. */
- rescan_dir = choose_scan_direction(imap[nblk_i],
- lfsparms->num_directions);
- /* If new scan direction is VERTICAL... */
- if(rescan_dir == SCAN_VERTICAL){
- /* Adjust scan_x, scan_y, scan_w, scan_h for rescan. */
- if((ret = adjust_vertical_rescan(nbr_dir, &rescan_x, &rescan_y,
- &rescan_w, &rescan_h,
- scan_x, scan_y, scan_w, scan_h, lfsparms->blocksize)))
- /* Return system error code. */
- return(ret);
- /* Rescan specified region in block vertically. */
- /* Pass IMAP direction for the block, NOT its neighbor. */
- if((ret = scan4minutiae_vertically(minutiae, bdata, iw, ih,
- imap[blk_i], nmap[blk_i],
- rescan_x, rescan_y, rescan_w, rescan_h, lfsparms)))
- /* Return code may be: */
- /* 1. ret<0 (implying system error) */
- return(ret);
- } /* Otherwise, block has already been scanned horizontally. */
- } /* Otherwise, neighbor has INVALID IMAP, so ignore rescan. */
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: get_nbr_block_index - Determines the block index (if one exists)
- #cat: for a specified neighbor of a block in the image.
- Input:
- nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
- blk_x - x-block coord to find neighbor of
- blk_y - y-block coord to find neighbor of
- mw - width (in blocks) of IMAP and NMAP matrices.
- mh - height (in blocks) of IMAP and NMAP matrices.
- Output:
- oblk_i - points to neighbor's block index
- Return Code:
- NOT_FOUND - neighbor index does not exist
- FOUND - neighbor index exists and returned
- Negative - system error
- **************************************************************************/
- int get_nbr_block_index(int *oblk_i, const int nbr_dir,
- const int blk_x, const int blk_y, const int mw, const int mh)
- {
- int nx, ny, ni;
- switch(nbr_dir){
- case NORTH:
- /* If neighbor doesn't exist above... */
- if((ny = blk_y-1) < 0)
- /* Done, so return normally. */
- return(NOT_FOUND);
- /* Get neighbor's block index. */
- ni = (ny*mw)+blk_x;
- break;
- case EAST:
- /* If neighbor doesn't exist to the right... */
- if((nx = blk_x+1) >= mw)
- /* Done, so return normally. */
- return(NOT_FOUND);
- /* Get neighbor's block index. */
- ni = (blk_y*mw)+nx;
- break;
- case SOUTH:
- /* If neighbor doesn't exist below... */
- if((ny = blk_y+1) >= mh)
- /* Return normally. */
- return(NOT_FOUND);
- /* Get neighbor's block index. */
- ni = (ny*mw)+blk_x;
- break;
- case WEST:
- /* If neighbor doesn't exist to the left... */
- if((nx = blk_x-1) < 0)
- /* Return normally. */
- return(NOT_FOUND);
- /* Get neighbor's block index. */
- ni = (blk_y*mw)+nx;
- break;
- default:
- fprintf(stderr,
- "ERROR : get_nbr_block_index : illegal neighbor direction\n");
- return(-200);
- }
- /* Assign output pointer. */
- *oblk_i = ni;
- /* Return neighbor FOUND. */
- return(FOUND);
- }
- /*************************************************************************
- **************************************************************************
- #cat: adjust_horizontal_rescan - Determines the portion of an image block to
- #cat: be rescanned horizontally based on a specified neighbor.
- Input:
- nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
- scan_x - x-pixel coord of origin of image region
- scan_y - y-pixel coord of origin of image region
- scan_w - width (in pixels) of image region
- scan_h - height (in pixels) of image region
- blocksize - dimension of image blocks (in pixels)
- Output:
- rescan_x - x-pixel coord of origin of region to be rescanned
- rescan_y - y-pixel coord of origin of region to be rescanned
- rescan_w - width (in pixels) of region to be rescanned
- rescan_h - height (in pixels) of region to be rescanned
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int adjust_horizontal_rescan(const int nbr_dir, int *rescan_x, int *rescan_y,
- int *rescan_w, int *rescan_h, const int scan_x, const int scan_y,
- const int scan_w, const int scan_h, const int blocksize)
- {
- int half_blocksize, qtr_blocksize;
- /* Compute half of blocksize. */
- half_blocksize = blocksize>>1;
- /* Compute quarter of blocksize. */
- qtr_blocksize = blocksize>>2;
- /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
- switch(nbr_dir){
- case NORTH:
- /*
- *************************
- * RESCAN NORTH *
- * AREA *
- *************************
- | |
- | |
- | |
- | |
- | |
- | |
- -------------------------
- */
- /* Rescan origin stays the same. */
- *rescan_x = scan_x;
- *rescan_y = scan_y;
- /* Rescan width stays the same. */
- *rescan_w = scan_w;
- /* Rescan height is reduced to "qtr_blocksize" */
- /* if scan_h is larger. */
- *rescan_h = min(qtr_blocksize, scan_h);
- break;
- case EAST:
- /*
- ------------*************
- | * *
- | * *
- | * E R *
- | * A E *
- | * S S *
- | * T C *
- | * A *
- | * N *
- | * *
- | * *
- ------------*************
- */
- /* Rescan x-orign is set to half_blocksize from right edge of */
- /* block if scan width is larger. */
- *rescan_x = max(scan_x+scan_w-half_blocksize, scan_x);
- /* Rescan y-origin stays the same. */
- *rescan_y = scan_y;
- /* Rescan width is reduced to "half_blocksize" */
- /* if scan width is larger. */
- *rescan_w = min(half_blocksize, scan_w);
- /* Rescan height stays the same. */
- *rescan_h = scan_h;
- break;
- case SOUTH:
- /*
- -------------------------
- | |
- | |
- | |
- | |
- | |
- | |
- *************************
- * RESCAN SOUTH *
- * AREA *
- *************************
- */
- /* Rescan x-origin stays the same. */
- *rescan_x = scan_x;
- /* Rescan y-orign is set to qtr_blocksize from bottom edge of */
- /* block if scan height is larger. */
- *rescan_y = max(scan_y+scan_h-qtr_blocksize, scan_y);
- /* Rescan width stays the same. */
- *rescan_w = scan_w;
- /* Rescan height is reduced to "qtr_blocksize" */
- /* if scan height is larger. */
- *rescan_h = min(qtr_blocksize, scan_h);
- break;
- case WEST:
- /*
- *************------------
- * * |
- * * |
- * W R * |
- * E E * |
- * S S * |
- * T C * |
- * A * |
- * N * |
- * * |
- * * |
- *************------------
- */
- /* Rescan origin stays the same. */
- *rescan_x = scan_x;
- *rescan_y = scan_y;
- /* Rescan width is reduced to "half_blocksize" */
- /* if scan width is larger. */
- *rescan_w = min(half_blocksize, scan_w);
- /* Rescan height stays the same. */
- *rescan_h = scan_h;
- break;
- default:
- fprintf(stderr,
- "ERROR : adjust_horizontal_rescan : illegal neighbor direction\n");
- return(-210);
- }
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: adjust_vertical_rescan - Determines the portion of an image block to
- #cat: be rescanned vertically based on a specified neighbor.
- Input:
- nbr_dir - specifies which block neighbor {NORTH, SOUTH, EAST, WEST}
- scan_x - x-pixel coord of origin of image region
- scan_y - y-pixel coord of origin of image region
- scan_w - width (in pixels) of image region
- scan_h - height (in pixels) of image region
- blocksize - dimension of image blocks (in pixels)
- Output:
- rescan_x - x-pixel coord of origin of region to be rescanned
- rescan_y - y-pixel coord of origin of region to be rescanned
- rescan_w - width (in pixels) of region to be rescanned
- rescan_h - height (in pixels) of region to be rescanned
- Return Code:
- Zero - successful completion
- Negative - system error
- **************************************************************************/
- int adjust_vertical_rescan(const int nbr_dir, int *rescan_x, int *rescan_y,
- int *rescan_w, int *rescan_h, const int scan_x, const int scan_y,
- const int scan_w, const int scan_h, const int blocksize)
- {
- int half_blocksize, qtr_blocksize;
- /* Compute half of blocksize. */
- half_blocksize = blocksize>>1;
- /* Compute quarter of blocksize. */
- qtr_blocksize = blocksize>>2;
- /* Neighbor will either be NORTH, SOUTH, EAST, OR WEST. */
- switch(nbr_dir){
- case NORTH:
- /*
- *************************
- * *
- * RESCAN NORTH *
- * AREA *
- * *
- *************************
- | |
- | |
- | |
- | |
- | |
- -------------------------
- */
- /* Rescan origin stays the same. */
- *rescan_x = scan_x;
- *rescan_y = scan_y;
- /* Rescan width stays the same. */
- *rescan_w = scan_w;
- /* Rescan height is reduced to "half_blocksize" */
- /* if scan_h is larger. */
- *rescan_h = min(half_blocksize, scan_h);
- break;
- case EAST:
- /*
- ------------------*******
- | * *
- | * *
- | * E R *
- | * A E *
- | * S S *
- | * T C *
- | * A *
- | * N *
- | * *
- | * *
- ------------------*******
- */
- /* Rescan x-orign is set to qtr_blocksize from right edge of */
- /* block if scan width is larger. */
- *rescan_x = max(scan_x+scan_w-qtr_blocksize, scan_x);
- /* Rescan y-origin stays the same. */
- *rescan_y = scan_y;
- /* Rescan width is reduced to "qtr_blocksize" */
- /* if scan width is larger. */
- *rescan_w = min(qtr_blocksize, scan_w);
- /* Rescan height stays the same. */
- *rescan_h = scan_h;
- break;
- case SOUTH:
- /*
- -------------------------
- | |
- | |
- | |
- | |
- | |
- *************************
- * *
- * RESCAN SOUTH *
- * AREA *
- * *
- *************************
- */
- /* Rescan x-origin stays the same. */
- *rescan_x = scan_x;
- /* Rescan y-orign is set to half_blocksize from bottom edge of */
- /* block if scan height is larger. */
- *rescan_y = max(scan_y+scan_h-half_blocksize, scan_y);
- /* Rescan width stays the same. */
- *rescan_w = scan_w;
- /* Rescan height is reduced to "half_blocksize" */
- /* if scan height is larger. */
- *rescan_h = min(half_blocksize, scan_h);
- break;
- case WEST:
- /*
- *******------------------
- * * |
- * * |
- * W R * |
- * E E * |
- * S S * |
- * T C * |
- * A * |
- * N * |
- * * |
- * * |
- *******------------------
- */
- /* Rescan origin stays the same. */
- *rescan_x = scan_x;
- *rescan_y = scan_y;
- /* Rescan width is reduced to "qtr_blocksize" */
- /* if scan width is larger. */
- *rescan_w = min(qtr_blocksize, scan_w);
- /* Rescan height stays the same. */
- *rescan_h = scan_h;
- break;
- default:
- fprintf(stderr,
- "ERROR : adjust_vertical_rescan : illegal neighbor direction\n");
- return(-220);
- }
- /* Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: process_horizontal_scan_minutia - Takes a minutia point that was
- #cat: detected via the horizontal scan process and
- #cat: adjusts its location (if necessary), determines its
- #cat: direction, and (if it is not already in the minutiae
- #cat: list) adds it to the list. These minutia are by nature
- #cat: vertical in orientation (orthogonal to the scan).
- Input:
- cx - x-pixel coord where 3rd pattern pair of mintuia was detected
- cy - y-pixel coord where 3rd pattern pair of mintuia was detected
- y2 - y-pixel coord where 2nd pattern pair of mintuia was detected
- feature_id - type of minutia (ex. index into feature_patterns[] list)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imapval - IMAP value associated with this image region
- nmapval - NMAP value associated with this image region
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- IGNORE - minutia is to be ignored
- Negative - system error
- **************************************************************************/
- int process_horizontal_scan_minutia(MINUTIAE *minutiae,
- const int cx, const int cy,
- const int x2, const int feature_id,
- unsigned char *bdata, const int iw, const int ih,
- const int imapval, const int nmapval,
- const LFSPARMS *lfsparms)
- {
- MINUTIA *minutia;
- int x_loc, y_loc;
- int x_edge, y_edge;
- int idir, ret;
- /* Set x location of minutia point to be half way between */
- /* first position of second feature pair and position of */
- /* third feature pair. */
- x_loc = (cx + x2)>>1;
- /* Set same x location to neighboring edge pixel. */
- x_edge = x_loc;
- /* Feature location should always point to either ending */
- /* of ridge or (for bifurcations) ending of valley. */
- /* So, if detected feature is APPEARING... */
- if(feature_patterns[feature_id].appearing){
- /* Set y location to second scan row. */
- y_loc = cy+1;
- /* Set y location of neighboring edge pixel to the first scan row. */
- y_edge = cy;
- }
- /* Otherwise, feature is DISAPPEARING... */
- else{
- /* Set y location to first scan row. */
- y_loc = cy;
- /* Set y location of neighboring edge pixel to the second scan row. */
- y_edge = cy+1;
- }
- /* If current minutia is in a high-curvature block... */
- if(nmapval == HIGH_CURVATURE){
- /* Adjust location and direction locally. */
- if((ret = adjust_high_curvature_minutia(&idir, &x_loc, &y_loc,
- &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
- bdata, iw, ih, minutiae, lfsparms))){
- /* Could be a system error or IGNORE minutia. */
- return(ret);
- }
- /* Otherwise, we have our high-curvature minutia attributes. */
- }
- /* Otherwise, minutia is in fairly low-curvature block... */
- else{
- /* Get minutia direction based on current IMAP value. */
- idir = get_low_curvature_direction(SCAN_HORIZONTAL,
- feature_patterns[feature_id].appearing,
- imapval, lfsparms->num_directions);
- }
- /* Create a minutia object based on derived attributes. */
- if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
- DEFAULT_RELIABILITY,
- feature_patterns[feature_id].type,
- feature_patterns[feature_id].appearing, feature_id)))
- /* Return system error. */
- return(ret);
- /* Update the minutiae list with potential new minutia. */
- ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
- /* If minuitia IGNORED and not added to the minutia list ... */
- if(ret == IGNORE)
- /* Deallocate the minutia. */
- free_minutia(minutia);
- /* Otherwise, return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: process_horizontal_scan_minutia_V2 - Takes a minutia point that was
- #cat: detected via the horizontal scan process and
- #cat: adjusts its location (if necessary), determines its
- #cat: direction, and (if it is not already in the minutiae
- #cat: list) adds it to the list. These minutia are by nature
- #cat: vertical in orientation (orthogonal to the scan).
- Input:
- cx - x-pixel coord where 3rd pattern pair of mintuia was detected
- cy - y-pixel coord where 3rd pattern pair of mintuia was detected
- y2 - y-pixel coord where 2nd pattern pair of mintuia was detected
- feature_id - type of minutia (ex. index into feature_patterns[] list)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- pdirection_map - pixelized Direction Map
- plow_flow_map - pixelized Low Ridge Flow Map
- phigh_curve_map - pixelized High Curvature Map
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- IGNORE - minutia is to be ignored
- Negative - system error
- **************************************************************************/
- int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
- const int cx, const int cy,
- const int x2, const int feature_id,
- unsigned char *bdata, const int iw, const int ih,
- int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
- const LFSPARMS *lfsparms)
- {
- MINUTIA *minutia;
- int x_loc, y_loc;
- int x_edge, y_edge;
- int idir, ret;
- int dmapval, fmapval, cmapval;
- double reliability;
- /* Set x location of minutia point to be half way between */
- /* first position of second feature pair and position of */
- /* third feature pair. */
- x_loc = (cx + x2)>>1;
- /* Set same x location to neighboring edge pixel. */
- x_edge = x_loc;
- /* Feature location should always point to either ending */
- /* of ridge or (for bifurcations) ending of valley. */
- /* So, if detected feature is APPEARING... */
- if(feature_patterns[feature_id].appearing){
- /* Set y location to second scan row. */
- y_loc = cy+1;
- /* Set y location of neighboring edge pixel to the first scan row. */
- y_edge = cy;
- }
- /* Otherwise, feature is DISAPPEARING... */
- else{
- /* Set y location to first scan row. */
- y_loc = cy;
- /* Set y location of neighboring edge pixel to the second scan row. */
- y_edge = cy+1;
- }
- dmapval = *(pdirection_map+(y_loc*iw)+x_loc);
- fmapval = *(plow_flow_map+(y_loc*iw)+x_loc);
- cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc);
- /* If the minutia point is in a block with INVALID direction ... */
- if(dmapval == INVALID_DIR)
- /* Then, IGNORE the point. */
- return(IGNORE);
- /* If current minutia is in a HIGH CURVATURE block ... */
- if(cmapval){
- /* Adjust location and direction locally. */
- if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc,
- &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
- bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){
- /* Could be a system error or IGNORE minutia. */
- return(ret);
- }
- /* Otherwise, we have our high-curvature minutia attributes. */
- }
- /* Otherwise, minutia is in fairly low-curvature block... */
- else{
- /* Get minutia direction based on current block's direction. */
- idir = get_low_curvature_direction(SCAN_HORIZONTAL,
- feature_patterns[feature_id].appearing, dmapval,
- lfsparms->num_directions);
- }
- /* If current minutia is in a LOW RIDGE FLOW block ... */
- if(fmapval)
- reliability = MEDIUM_RELIABILITY;
- else
- /* Otherwise, minutia is in a block with reliable direction and */
- /* binarization. */
- reliability = HIGH_RELIABILITY;
- /* Create a minutia object based on derived attributes. */
- if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
- reliability,
- feature_patterns[feature_id].type,
- feature_patterns[feature_id].appearing, feature_id)))
- /* Return system error. */
- return(ret);
- /* Update the minutiae list with potential new minutia. */
- ret = update_minutiae_V2(minutiae, minutia, SCAN_HORIZONTAL,
- dmapval, bdata, iw, ih, lfsparms);
- /* If minuitia IGNORED and not added to the minutia list ... */
- if(ret == IGNORE)
- /* Deallocate the minutia. */
- free_minutia(minutia);
- /* Otherwise, return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: process_vertical_scan_minutia - Takes a minutia point that was
- #cat: detected in via the vertical scan process and
- #cat: adjusts its location (if necessary), determines its
- #cat: direction, and (if it is not already in the minutiae
- #cat: list) adds it to the list. These minutia are by nature
- #cat: horizontal in orientation (orthogonal to the scan).
- Input:
- cx - x-pixel coord where 3rd pattern pair of mintuia was detected
- cy - y-pixel coord where 3rd pattern pair of mintuia was detected
- x2 - x-pixel coord where 2nd pattern pair of mintuia was detected
- feature_id - type of minutia (ex. index into feature_patterns[] list)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- imapval - IMAP value associated with this image region
- nmapval - NMAP value associated with this image region
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- IGNORE - minutia is to be ignored
- Negative - system error
- **************************************************************************/
- int process_vertical_scan_minutia(MINUTIAE *minutiae,
- const int cx, const int cy,
- const int y2, const int feature_id,
- unsigned char *bdata, const int iw, const int ih,
- const int imapval, const int nmapval,
- const LFSPARMS *lfsparms)
- {
- MINUTIA *minutia;
- int x_loc, y_loc;
- int x_edge, y_edge;
- int idir, ret;
- /* Feature location should always point to either ending */
- /* of ridge or (for bifurcations) ending of valley. */
- /* So, if detected feature is APPEARING... */
- if(feature_patterns[feature_id].appearing){
- /* Set x location to second scan column. */
- x_loc = cx+1;
- /* Set x location of neighboring edge pixel to the first scan column. */
- x_edge = cx;
- }
- /* Otherwise, feature is DISAPPEARING... */
- else{
- /* Set x location to first scan column. */
- x_loc = cx;
- /* Set x location of neighboring edge pixel to the second scan column. */
- x_edge = cx+1;
- }
- /* Set y location of minutia point to be half way between */
- /* first position of second feature pair and position of */
- /* third feature pair. */
- y_loc = (cy + y2)>>1;
- /* Set same y location to neighboring edge pixel. */
- y_edge = y_loc;
- /* If current minutia is in a high-curvature block... */
- if(nmapval == HIGH_CURVATURE){
- /* Adjust location and direction locally. */
- if((ret = adjust_high_curvature_minutia(&idir, &x_loc, &y_loc,
- &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
- bdata, iw, ih, minutiae, lfsparms))){
- /* Could be a system error or IGNORE minutia. */
- return(ret);
- }
- /* Otherwise, we have our high-curvature minutia attributes. */
- }
- /* Otherwise, minutia is in fairly low-curvature block... */
- else{
- /* Get minutia direction based on current IMAP value. */
- idir = get_low_curvature_direction(SCAN_VERTICAL,
- feature_patterns[feature_id].appearing,
- imapval, lfsparms->num_directions);
- }
- /* Create a minutia object based on derived attributes. */
- if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
- DEFAULT_RELIABILITY,
- feature_patterns[feature_id].type,
- feature_patterns[feature_id].appearing, feature_id)))
- /* Return system error. */
- return(ret);
- /* Update the minutiae list with potential new minutia. */
- ret = update_minutiae(minutiae, minutia, bdata, iw, ih, lfsparms);
- /* If minuitia IGNORED and not added to the minutia list ... */
- if(ret == IGNORE)
- /* Deallocate the minutia. */
- free_minutia(minutia);
- /* Otherwise, return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: process_vertical_scan_minutia_V2 - Takes a minutia point that was
- #cat: detected in via the vertical scan process and
- #cat: adjusts its location (if necessary), determines its
- #cat: direction, and (if it is not already in the minutiae
- #cat: list) adds it to the list. These minutia are by nature
- #cat: horizontal in orientation (orthogonal to the scan).
- Input:
- cx - x-pixel coord where 3rd pattern pair of mintuia was detected
- cy - y-pixel coord where 3rd pattern pair of mintuia was detected
- x2 - x-pixel coord where 2nd pattern pair of mintuia was detected
- feature_id - type of minutia (ex. index into feature_patterns[] list)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- pdirection_map - pixelized Direction Map
- plow_flow_map - pixelized Low Ridge Flow Map
- phigh_curve_map - pixelized High Curvature Map
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - successful completion
- IGNORE - minutia is to be ignored
- Negative - system error
- **************************************************************************/
- int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
- const int cx, const int cy,
- const int y2, const int feature_id,
- unsigned char *bdata, const int iw, const int ih,
- int *pdirection_map, int *plow_flow_map, int *phigh_curve_map,
- const LFSPARMS *lfsparms)
- {
- MINUTIA *minutia;
- int x_loc, y_loc;
- int x_edge, y_edge;
- int idir, ret;
- int dmapval, fmapval, cmapval;
- double reliability;
- /* Feature location should always point to either ending */
- /* of ridge or (for bifurcations) ending of valley. */
- /* So, if detected feature is APPEARING... */
- if(feature_patterns[feature_id].appearing){
- /* Set x location to second scan column. */
- x_loc = cx+1;
- /* Set x location of neighboring edge pixel to the first scan column. */
- x_edge = cx;
- }
- /* Otherwise, feature is DISAPPEARING... */
- else{
- /* Set x location to first scan column. */
- x_loc = cx;
- /* Set x location of neighboring edge pixel to the second scan column. */
- x_edge = cx+1;
- }
- /* Set y location of minutia point to be half way between */
- /* first position of second feature pair and position of */
- /* third feature pair. */
- y_loc = (cy + y2)>>1;
- /* Set same y location to neighboring edge pixel. */
- y_edge = y_loc;
- dmapval = *(pdirection_map+(y_loc*iw)+x_loc);
- fmapval = *(plow_flow_map+(y_loc*iw)+x_loc);
- cmapval = *(phigh_curve_map+(y_loc*iw)+x_loc);
- /* If the minutia point is in a block with INVALID direction ... */
- if(dmapval == INVALID_DIR)
- /* Then, IGNORE the point. */
- return(IGNORE);
- /* If current minutia is in a HIGH CURVATURE block... */
- if(cmapval){
- /* Adjust location and direction locally. */
- if((ret = adjust_high_curvature_minutia_V2(&idir, &x_loc, &y_loc,
- &x_edge, &y_edge, x_loc, y_loc, x_edge, y_edge,
- bdata, iw, ih, plow_flow_map, minutiae, lfsparms))){
- /* Could be a system error or IGNORE minutia. */
- return(ret);
- }
- /* Otherwise, we have our high-curvature minutia attributes. */
- }
- /* Otherwise, minutia is in fairly low-curvature block... */
- else{
- /* Get minutia direction based on current block's direction. */
- idir = get_low_curvature_direction(SCAN_VERTICAL,
- feature_patterns[feature_id].appearing, dmapval,
- lfsparms->num_directions);
- }
- /* If current minutia is in a LOW RIDGE FLOW block ... */
- if(fmapval)
- reliability = MEDIUM_RELIABILITY;
- else
- /* Otherwise, minutia is in a block with reliable direction and */
- /* binarization. */
- reliability = HIGH_RELIABILITY;
- /* Create a minutia object based on derived attributes. */
- if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
- reliability,
- feature_patterns[feature_id].type,
- feature_patterns[feature_id].appearing, feature_id)))
- /* Return system error. */
- return(ret);
- /* Update the minutiae list with potential new minutia. */
- ret = update_minutiae_V2(minutiae, minutia, SCAN_VERTICAL,
- dmapval, bdata, iw, ih, lfsparms);
- /* If minuitia IGNORED and not added to the minutia list ... */
- if(ret == IGNORE)
- /* Deallocate the minutia. */
- free_minutia(minutia);
- /* Otherwise, return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: adjust_high_curvature_minutia - Takes an initial minutia point detected
- #cat: in a high-curvature area and adjusts its location and
- #cat: direction. First, it walks and extracts the contour
- #cat: of the detected feature looking for and processing any loop
- #cat: discovered along the way. Once the contour is extracted,
- #cat: the point of highest-curvature is determined and used to
- #cat: adjust the location of the minutia point. The angle of
- #cat: the line perpendicular to the tangent on the high-curvature
- #cat: contour at the minutia point is used as the mintutia's
- #cat: direction.
- Input:
- x_loc - starting x-pixel coord of feature (interior to feature)
- y_loc - starting y-pixel coord of feature (interior to feature)
- x_edge - x-pixel coord of corresponding edge pixel
- (exterior to feature)
- y_edge - y-pixel coord of corresponding edge pixel
- (exterior to feature)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- oidir - direction of adjusted minutia point
- ox_loc - adjusted x-pixel coord of feature
- oy_loc - adjusted y-pixel coord of feature
- ox_edge - adjusted x-pixel coord of corresponding edge pixel
- oy_edge - adjusted y-pixel coord of corresponding edge pixel
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - minutia point processed successfully
- IGNORE - minutia point is to be ignored
- Negative - system error
- **************************************************************************/
- int adjust_high_curvature_minutia(int *oidir, int *ox_loc, int *oy_loc,
- int *ox_edge, int *oy_edge,
- const int x_loc, const int y_loc,
- const int x_edge, const int y_edge,
- unsigned char *bdata, const int iw, const int ih,
- MINUTIAE *minutiae, const LFSPARMS *lfsparms)
- {
- int ret;
- int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
- int min_i;
- double min_theta;
- int feature_pix;
- int mid_x, mid_y, mid_pix;
- int idir;
- int half_contour, angle_edge;
- /* Set variable from parameter structure. */
- half_contour = lfsparms->high_curve_half_contour;
- /* Set edge length for computing contour's angle of curvature */
- /* to one quarter of desired pixel length of entire contour. */
- /* Ex. If half_contour==14, then contour length==29=(2X14)+1 */
- /* and angle_edge==7=(14/2). */
- angle_edge = half_contour>>1;
- /* Get the pixel value of current feature. */
- feature_pix = *(bdata + (y_loc * iw) + x_loc);
- /* Extract feature's contour. */
- if((ret = get_high_curvature_contour(&contour_x, &contour_y,
- &contour_ex, &contour_ey, &ncontour, half_contour,
- x_loc, y_loc, x_edge, y_edge, bdata, iw, ih))){
- /* Returns with: */
- /* 1. Successful or empty contour == 0 */
- /* If contour is empty, then contour lists are not allocated. */
- /* 2. Contour forms loop == LOOP_FOUND */
- /* 3. Sysetm error < 0 */
- /* If the contour forms a loop... */
- if(ret == LOOP_FOUND){
- /* If the order of the contour is clockwise, then the loops's */
- /* contour pixels are outside the corresponding edge pixels. We */
- /* definitely do NOT want to fill based on the feature pixel in */
- /* this case, because it is OUTSIDE the loop. For now we will */
- /* ignore the loop and the minutia that triggered its tracing. */
- /* It is likely that other minutia on the loop will be */
- /* detected that create a contour on the "inside" of the loop. */
- /* There is another issue here that could be addressed ... */
- /* It seems that many/multiple minutia are often detected within */
- /* the same loop, which currently requires retracing the loop, */
- /* locating minutia on opposite ends of the major axis of the */
- /* loop, and then determining that the minutia have already been */
- /* entered into the minutiae list upon processing the very first */
- /* minutia detected in the loop. There is a lot of redundant */
- /* work being done here! */
- /* Is_loop_clockwise takes a default value to be returned if the */
- /* routine is unable to determine the direction of the contour. */
- /* In this case, we want to IGNORE the loop if we can't tell its */
- /* direction so that we do not inappropriately fill the loop, so */
- /* we are passing the default value TRUE. */
- if((ret = is_loop_clockwise(contour_x, contour_y, ncontour, TRUE))){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* If we had a system error... */
- if(ret < 0)
- /* Return the error code. */
- return(ret);
- /* Otherwise, loop is clockwise, so return IGNORE. */
- return(IGNORE);
- }
- /* Otherwise, process the clockwise-ordered contour of the loop */
- /* as it may contain minutia. If no minutia found, then it is */
- /* filled in. */
- ret = process_loop(minutiae, contour_x, contour_y,
- contour_ex, contour_ey, ncontour,
- bdata, iw, ih, lfsparms);
- /* Returns with: */
- /* 1. Successful processing of loop == 0 */
- /* 2. System error < 0 */
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* If loop processed successfully ... */
- if(ret == 0)
- /* Then either a minutia pair was extracted or the loop was */
- /* filled. Either way we want to IGNORE the minutia that */
- /* started the whole loop processing in the beginning. */
- return(IGNORE);
- /* Otherwise, there was a system error. */
- /* Return the resulting code. */
- return(ret);
- }
- /* Otherwise not a loop, so get_high_curvature_contour incurred */
- /* a system error. Return the error code. */
- return(ret);
- }
- /* If contour is empty ... then contour lists were not allocated, so */
- /* simply return IGNORE. The contour comes back empty when there */
- /* were not a sufficient number of points found on the contour. */
- if(ncontour == 0)
- return(IGNORE);
- /* Otherwise, there are contour points to process. */
- /* Given the contour, determine the point of highest curvature */
- /* (ie. forming the minimum angle between contour walls). */
- if((ret = min_contour_theta(&min_i, &min_theta, angle_edge,
- contour_x, contour_y, ncontour))){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Returns IGNORE or system error. Either way */
- /* free the contour and return the code. */
- return(ret);
- }
- /* If the minimum theta found along the contour is too large... */
- if(min_theta >= lfsparms->max_high_curve_theta){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Reject the high-curvature minutia, and return IGNORE. */
- return(IGNORE);
- }
- /* Test to see if interior of curvature is OK. Compute midpoint */
- /* between left and right points symmetrically distant (angle_edge */
- /* pixels) from the contour's point of minimum theta. */
- mid_x = (contour_x[min_i-angle_edge] + contour_x[min_i+angle_edge])>>1;
- mid_y = (contour_y[min_i-angle_edge] + contour_y[min_i+angle_edge])>>1;
- mid_pix = *(bdata + (mid_y * iw) + mid_x);
- /* If the interior pixel value is not the same as the feature's... */
- if(mid_pix != feature_pix){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Reject the high-curvature minutia and return IGNORE. */
- return(IGNORE);
- }
- /* Compute new direction based on line connecting adjusted feature */
- /* location and the midpoint in the feature's interior. */
- idir = line2direction(contour_x[min_i], contour_y[min_i],
- mid_x, mid_y, lfsparms->num_directions);
- /* Set minutia location to minimum theta position on the contour. */
- *oidir = idir;
- *ox_loc = contour_x[min_i];
- *oy_loc = contour_y[min_i];
- *ox_edge = contour_ex[min_i];
- *oy_edge = contour_ey[min_i];
- /* Deallocate contour buffers. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /*Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: adjust_high_curvature_minutia_V2 - Takes an initial minutia point
- #cat: in a high-curvature area and adjusts its location and
- #cat: direction. First, it walks and extracts the contour
- #cat: of the detected feature looking for and processing any loop
- #cat: discovered along the way. Once the contour is extracted,
- #cat: the point of highest-curvature is determined and used to
- #cat: adjust the location of the minutia point. The angle of
- #cat: the line perpendicular to the tangent on the high-curvature
- #cat: contour at the minutia point is used as the mintutia's
- #cat: direction.
- Input:
- x_loc - starting x-pixel coord of feature (interior to feature)
- y_loc - starting y-pixel coord of feature (interior to feature)
- x_edge - x-pixel coord of corresponding edge pixel
- (exterior to feature)
- y_edge - y-pixel coord of corresponding edge pixel
- (exterior to feature)
- bdata - binary image data (0==while & 1==black)
- iw - width (in pixels) of image
- ih - height (in pixels) of image
- plow_flow_map - pixelized Low Ridge Flow Map
- lfsparms - parameters and thresholds for controlling LFS
- Output:
- oidir - direction of adjusted minutia point
- ox_loc - adjusted x-pixel coord of feature
- oy_loc - adjusted y-pixel coord of feature
- ox_edge - adjusted x-pixel coord of corresponding edge pixel
- oy_edge - adjusted y-pixel coord of corresponding edge pixel
- minutiae - points to a list of detected minutia structures
- Return Code:
- Zero - minutia point processed successfully
- IGNORE - minutia point is to be ignored
- Negative - system error
- **************************************************************************/
- int adjust_high_curvature_minutia_V2(int *oidir, int *ox_loc, int *oy_loc,
- int *ox_edge, int *oy_edge,
- const int x_loc, const int y_loc,
- const int x_edge, const int y_edge,
- unsigned char *bdata, const int iw, const int ih,
- int *plow_flow_map, MINUTIAE *minutiae, const LFSPARMS *lfsparms)
- {
- int ret;
- int *contour_x, *contour_y, *contour_ex, *contour_ey, ncontour;
- int min_i;
- double min_theta;
- int feature_pix;
- int mid_x, mid_y, mid_pix;
- int idir;
- int half_contour, angle_edge;
- /* Set variable from parameter structure. */
- half_contour = lfsparms->high_curve_half_contour;
- /* Set edge length for computing contour's angle of curvature */
- /* to one quarter of desired pixel length of entire contour. */
- /* Ex. If half_contour==14, then contour length==29=(2X14)+1 */
- /* and angle_edge==7=(14/2). */
- angle_edge = half_contour>>1;
- /* Get the pixel value of current feature. */
- feature_pix = *(bdata + (y_loc * iw) + x_loc);
- /* Extract feature's contour. */
- if((ret = get_high_curvature_contour(&contour_x, &contour_y,
- &contour_ex, &contour_ey, &ncontour, half_contour,
- x_loc, y_loc, x_edge, y_edge, bdata, iw, ih))){
- /* Returns with: */
- /* 1. Successful or empty contour == 0 */
- /* If contour is empty, then contour lists are not allocated. */
- /* 2. Contour forms loop == LOOP_FOUND */
- /* 3. Sysetm error < 0 */
- /* If the contour forms a loop... */
- if(ret == LOOP_FOUND){
- /* If the order of the contour is clockwise, then the loops's */
- /* contour pixels are outside the corresponding edge pixels. We */
- /* definitely do NOT want to fill based on the feature pixel in */
- /* this case, because it is OUTSIDE the loop. For now we will */
- /* ignore the loop and the minutia that triggered its tracing. */
- /* It is likely that other minutia on the loop will be */
- /* detected that create a contour on the "inside" of the loop. */
- /* There is another issue here that could be addressed ... */
- /* It seems that many/multiple minutia are often detected within */
- /* the same loop, which currently requires retracing the loop, */
- /* locating minutia on opposite ends of the major axis of the */
- /* loop, and then determining that the minutia have already been */
- /* entered into the minutiae list upon processing the very first */
- /* minutia detected in the loop. There is a lot of redundant */
- /* work being done here! */
- /* Is_loop_clockwise takes a default value to be returned if the */
- /* routine is unable to determine the direction of the contour. */
- /* In this case, we want to IGNORE the loop if we can't tell its */
- /* direction so that we do not inappropriately fill the loop, so */
- /* we are passing the default value TRUE. */
- if((ret = is_loop_clockwise(contour_x, contour_y, ncontour, TRUE))){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* If we had a system error... */
- if(ret < 0)
- /* Return the error code. */
- return(ret);
- /* Otherwise, loop is clockwise, so return IGNORE. */
- return(IGNORE);
- }
- /* Otherwise, process the clockwise-ordered contour of the loop */
- /* as it may contain minutia. If no minutia found, then it is */
- /* filled in. */
- ret = process_loop_V2(minutiae, contour_x, contour_y,
- contour_ex, contour_ey, ncontour,
- bdata, iw, ih, plow_flow_map, lfsparms);
- /* Returns with: */
- /* 1. Successful processing of loop == 0 */
- /* 2. System error < 0 */
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* If loop processed successfully ... */
- if(ret == 0)
- /* Then either a minutia pair was extracted or the loop was */
- /* filled. Either way we want to IGNORE the minutia that */
- /* started the whole loop processing in the beginning. */
- return(IGNORE);
- /* Otherwise, there was a system error. */
- /* Return the resulting code. */
- return(ret);
- }
- /* Otherwise not a loop, so get_high_curvature_contour incurred */
- /* a system error. Return the error code. */
- return(ret);
- }
- /* If contour is empty ... then contour lists were not allocated, so */
- /* simply return IGNORE. The contour comes back empty when there */
- /* were not a sufficient number of points found on the contour. */
- if(ncontour == 0)
- return(IGNORE);
- /* Otherwise, there are contour points to process. */
- /* Given the contour, determine the point of highest curvature */
- /* (ie. forming the minimum angle between contour walls). */
- if((ret = min_contour_theta(&min_i, &min_theta, angle_edge,
- contour_x, contour_y, ncontour))){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Returns IGNORE or system error. Either way */
- /* free the contour and return the code. */
- return(ret);
- }
- /* If the minimum theta found along the contour is too large... */
- if(min_theta >= lfsparms->max_high_curve_theta){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Reject the high-curvature minutia, and return IGNORE. */
- return(IGNORE);
- }
- /* Test to see if interior of curvature is OK. Compute midpoint */
- /* between left and right points symmetrically distant (angle_edge */
- /* pixels) from the contour's point of minimum theta. */
- mid_x = (contour_x[min_i-angle_edge] + contour_x[min_i+angle_edge])>>1;
- mid_y = (contour_y[min_i-angle_edge] + contour_y[min_i+angle_edge])>>1;
- mid_pix = *(bdata + (mid_y * iw) + mid_x);
- /* If the interior pixel value is not the same as the feature's... */
- if(mid_pix != feature_pix){
- /* Deallocate contour lists. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /* Reject the high-curvature minutia and return IGNORE. */
- return(IGNORE);
- }
- /* Compute new direction based on line connecting adjusted feature */
- /* location and the midpoint in the feature's interior. */
- idir = line2direction(contour_x[min_i], contour_y[min_i],
- mid_x, mid_y, lfsparms->num_directions);
- /* Set minutia location to minimum theta position on the contour. */
- *oidir = idir;
- *ox_loc = contour_x[min_i];
- *oy_loc = contour_y[min_i];
- *ox_edge = contour_ex[min_i];
- *oy_edge = contour_ey[min_i];
- /* Deallocate contour buffers. */
- free_contour(contour_x, contour_y, contour_ex, contour_ey);
- /*Return normally. */
- return(0);
- }
- /*************************************************************************
- **************************************************************************
- #cat: get_low_curvature_direction - Converts a bi-direcitonal IMAP direction
- #cat: (based on a semi-circle) to a uni-directional value covering
- #cat: a full circle based on the scan orientation used to detect
- #cat: a minutia feature (horizontal or vertical) and whether the
- #cat: detected minutia is appearing or disappearing.
- Input:
- scan_dir - designates the feature scan orientation
- appearing - designates the minutia as appearing or disappearing
- imapval - IMAP block direction
- ndirs - number of IMAP directions (in semicircle)
- Return Code:
- New direction - bi-directonal integer direction on full circle
- *************************************************************************/
- int get_low_curvature_direction(const int scan_dir, const int appearing,
- const int imapval, const int ndirs)
- {
- int idir;
- /* Start direction out with IMAP value. */
- idir = imapval;
- /* NOTE! */
- /* The logic in this routine should hold whether for ridge endings */
- /* or for bifurcations. The examples in the comments show ridge */
- /* ending conditions only. */
- /* CASE I : Ridge flow in Quadrant I; directions [0..8] */
- if(imapval <= (ndirs>>1)){
- /* I.A: HORIZONTAL scan */
- if(scan_dir == SCAN_HORIZONTAL){
- /* I.A.1: Appearing Minutia */
- if(appearing){
- /* Ex. 0 0 0 */
- /* 0 1 0 */
- /* ? ? */
- /* Ridge flow is up and to the right, whereas */
- /* actual ridge is running down and to the */
- /* left. */
- /* Thus: HORIZONTAL : appearing : should be */
- /* OPPOSITE the ridge flow direction. */
- idir += ndirs;
- }
- /* Otherwise: */
- /* I.A.2: Disappearing Minutia */
- /* Ex. ? ? */
- /* 0 1 0 */
- /* 0 0 0 */
- /* Ridge flow is up and to the right, which */
- /* should be SAME direction from which ridge */
- /* is projecting. */
- /* Thus: HORIZONTAL : disappearing : should */
- /* be the same as ridge flow direction. */
- } /* End if HORIZONTAL scan */
- /* Otherwise: */
- /* I.B: VERTICAL scan */
- else{
- /* I.B.1: Disappearing Minutia */
- if(!appearing){
- /* Ex. 0 0 */
- /* ? 1 0 */
- /* ? 0 0 */
- /* Ridge flow is up and to the right, whereas */
- /* actual ridge is projecting down and to the */
- /* left. */
- /* Thus: VERTICAL : disappearing : should be */
- /* OPPOSITE the ridge flow direction. */
- idir += ndirs;
- }
- /* Otherwise: */
- /* I.B.2: Appearing Minutia */
- /* Ex. 0 0 ? */
- /* 0 1 ? */
- /* 0 0 */
- /* Ridge flow is up and to the right, which */
- /* should be SAME direction the ridge is */
- /* running. */
- /* Thus: VERTICAL : appearing : should be */
- /* be the same as ridge flow direction. */
- } /* End else VERTICAL scan */
- } /* End if Quadrant I */
- /* Otherwise: */
- /* CASE II : Ridge flow in Quadrant II; directions [9..15] */
- else{
- /* II.A: HORIZONTAL scan */
- if(scan_dir == SCAN_HORIZONTAL){
- /* II.A.1: Disappearing Minutia */
- if(!appearing){
- /* Ex. ? ? */
- /* 0 1 0 */
- /* 0 0 0 */
- /* Ridge flow is down and to the right, */
- /* whereas actual ridge is running up and to */
- /* the left. */
- /* Thus: HORIZONTAL : disappearing : should */
- /* be OPPOSITE the ridge flow direction.*/
- idir += ndirs;
- }
- /* Otherwise: */
- /* II.A.2: Appearing Minutia */
- /* Ex. 0 0 0 */
- /* 0 1 0 */
- /* ? ? */
- /* Ridge flow is down and to the right, which */
- /* should be same direction from which ridge */
- /* is projecting. */
- /* Thus: HORIZONTAL : appearing : should be */
- /* the SAME as ridge flow direction. */
- } /* End if HORIZONTAL scan */
- /* Otherwise: */
- /* II.B: VERTICAL scan */
- else{
- /* II.B.1: Disappearing Minutia */
- if(!appearing){
- /* Ex. ? 0 0 */
- /* ? 1 0 */
- /* 0 0 */
- /* Ridge flow is down and to the right, */
- /* whereas actual ridge is running up and to */
- /* the left. */
- /* Thus: VERTICAL : disappearing : should be */
- /* OPPOSITE the ridge flow direction. */
- idir += ndirs;
- }
- /* Otherwise: */
- /* II.B.2: Appearing Minutia */
- /* Ex. 0 0 */
- /* 0 1 ? */
- /* 0 0 ? */
- /* Ridge flow is down and to the right, which */
- /* should be same direction the ridge is */
- /* projecting. */
- /* Thus: VERTICAL : appearing : should be */
- /* be the SAME as ridge flow direction. */
- } /* End else VERTICAL scan */
- } /* End else Quadrant II */
- /* Return resulting direction on range [0..31]. */
- return(idir);
- }
|