2 ############################################################################
3 # Copyright (C) 2008 by Lukas Sandström #
6 # This program is free software; you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation; either version 2 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the #
18 # Free Software Foundation, Inc., #
19 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
20 ############################################################################
22 import pyfprint_swig
as pyf
25 # exceptions, especially for RETRY_* errors
26 # constants for fingers
30 # Image(img) for devices which don't support imaging? Is img NULL?
39 """Call this before doing anything else."""
40 _init_ok
= (pyf
.fp_init() == 0)
42 raise "fprint initialization failed."
45 """pyfprint can't be used after this is called."""
49 # """Enumeration of the different fingers, used when using Fprint.save_to_disk()."""
52 LEFT_THUMB
= pyf
.LEFT_THUMB
,
53 LEFT_INDEX
= pyf
.LEFT_INDEX
,
54 LEFT_MIDDLE
= pyf
.LEFT_MIDDLE
,
55 LEFT_RING
= pyf
.LEFT_RING
,
56 LEFT_LITTLE
= pyf
.LEFT_LITTLE
,
57 RIGHT_THUMB
= pyf
.RIGHT_THUMB
,
58 RIGHT_INDEX
= pyf
.RIGHT_INDEX
,
59 RIGHT_MIDDLE
= pyf
.RIGHT_MIDDLE
,
60 RIGHT_RING
= pyf
.RIGHT_RING
,
61 RIGHT_LITTLE
= pyf
.RIGHT_LITTLE
65 """Provides access to a fingerprint reading device. Don't construct this
66 directly, use discover_devices() instead."""
67 def __init__(self
, dev_ptr
= None, dscv_ptr
= None, DscvList
= None):
68 """For internal use only."""
71 self
.DscvList
= DscvList
72 if dscv_ptr
and DscvList
== None:
73 raise "Programming error? Device contructed with dscv without DscvList."
76 """Closes the device. No more methods, except open(), may be called after this."""
78 pyf
.fp_dev_close(self
.dev
)
82 """Connects to the device."""
84 raise "Device already open"
85 self
.dev
= pyf
.fp_dev_open(self
.dscv
)
87 raise "device open failed"
91 Return a Driver instance.
93 open() is not required before this method.
96 return Driver(pyf
.fp_dev_get_driver(self
.dev
))
98 return Driver(pyf
.fp_dscv_dev_get_driver(self
.dscv
))
102 Return an integer representing the type of device.
104 open() is not required before this method.
107 return pyf
.fp_dev_get_devtype(self
.dev
)
109 return pyf
.fp_dscv_dev_get_devtype(self
.dev
)
111 def nr_enroll_stages(self
):
113 Return how many times enroll_finger needs to be called
114 before the finger is successfully enrolled.
117 return pyf
.fp_dev_get_nr_enroll_stages(self
.dev
)
118 raise "Device not open"
120 def is_compatible(self
, fprint
):
122 Checks whether the passed fprint is compatible with the device.
124 open() is not required before this method.
128 return pyf
.fp_dev_supports_print_data(self
.dev
, fprint
.data_ptr
) == 1
130 return pyf
.fp_dev_supports_dscv_print(self
.dev
, fprint
.dscv_ptr
) == 1
131 raise "No print found"
134 return pyf
.fp_dscv_dev_supports_print_data(self
.dscv
, fprint
.data_ptr
) == 1
136 return pyf
.fp_dscv_dev_supports_dscv_print(self
.dscv
, fprint
.dscv_ptr
) == 1
137 raise "No print found"
138 raise "No device found"
140 def supports_imaging(self
):
141 """If true, the device can return an image of the finger."""
143 return pyf
.fp_dev_supports_imaging(self
.dev
) == 1
144 raise "Device not open"
147 """Return the width of the images scanned by the device, in pixels."""
149 return pyf
.fp_dev_get_img_width(self
.dev
)
150 raise "Device not open"
152 def img_height(self
):
153 """Return the height of the images scanned by the device, in pixels."""
155 return pyf
.fp_dev_get_img_height(self
.dev
)
156 raise "Device not open"
158 def capture_image(self
, wait_for_finger
):
160 Captures an image from the device. Return None if imaging isn't supported.
161 wait_for_finger controls if the device should wait for a finger to be placed
162 on the sensor before image capture.
165 raise "Device not open"
167 if not self
.supports_imaging():
171 if wait_for_finger
== True:
174 (r
, img
) = pyf
.pyfp_dev_img_capture(self
.dev
, unconditional
)
177 raise "image_capture failed. error: %i" % r
180 def enroll_finger(self
):
181 """FIXME: docstring, error handling"""
183 raise "Device not open"
184 (r
, fprint
, img
) = pyf
.pyfp_enroll_finger_img(self
.dev
)
187 raise "Internal I/O error while enrolling: %i" % i
188 if r
== pyf
.FP_ENROLL_COMPLETE
:
189 _dbg("enroll complete")
190 return (Fprint(data_ptr
= fprint
), img
)
191 if r
== pyf
.FP_ENROLL_FAIL
:
192 print "Failed. Enrollmet process reset."
193 if r
== pyf
.FP_ENROLL_PASS
:
196 if r
== pyf
.FP_ENROLL_RETRY
:
199 if r
== pyf
.FP_ENROLL_RETRY_TOO_SHORT
:
200 _dbg("enroll RETRY_SHORT")
202 if r
== pyf
.FP_ENROLL_RETRY_CENTER_FINGER
:
203 _dbg("enroll RETRY_CENTER")
205 if r
== pyf
.FP_ENROLL_RETRY_REMOVE_FINGER
:
206 _dbg("enroll RETRY_REMOVE")
210 def verify_finger(self
, fprint
):
212 Compare the finger on the device with the supplied Fprint.
213 Return true if the finger and the Fprint matches.
216 raise "Device not open"
217 (r
, img
) = pyf
.pyfp_verify_finger_img(self
.dev
, fprint
._get
_print
_data
_ptr
())
220 raise "verify error: %i" % r
221 if r
== pyf
.FP_VERIFY_NO_MATCH
:
223 if r
== pyf
.FP_VERIFY_MATCH
:
225 if r
== pyf
.FP_VERIFY_RETRY
:
227 if r
== pyf
.FP_VERIFY_RETRY_TOO_SHORT
:
229 if r
== pyf
.FP_VERIFY_RETRY_CENTER_FINGER
:
231 if r
== pyf
.FP_VERIFY_RETRY_REMOVE_FINGER
:
235 def supports_identification(self
):
236 """Return True if the device supports the identify_finger method."""
238 raise "Device not open"
239 return pyf
.fp_dev_supports_identification(self
.dev
) == 1
241 def identify_finger(self
, fprints
):
243 FIXME: error handling
245 Match the finger on the reader against a list of Fprints.
247 Return a tuple: (list_offset, Fprint, Image) if a match is found,
248 (None, None, Image) otherwise.
250 Image is None if the device doesn't support imaging.
254 raise "Device not open"
255 gallery
= pyf
.pyfp_print_data_array(len(fprints
))
257 if not self
.is_compatible(x
):
258 raise "can't verify uncompatible print"
259 gallery
.append(x
._get
_print
_data
_ptr
())
260 (r
, offset
, img
) = pyf
.pyfp_identify_finger_img(self
.dev
, gallery
.list)
263 raise "identification error"
264 if r
== pyf
.FP_VERIFY_NO_MATCH
:
265 return (None, None, img
)
266 if r
== pyf
.FP_VERIFY_MATCH
:
267 return (offset
, fprints
[offset
], img
)
268 if r
== pyf
.FP_VERIFY_RETRY
:
270 if r
== pyf
.FP_VERIFY_RETRY_TOO_SHORT
:
272 if r
== pyf
.FP_VERIFY_RETRY_CENTER_FINGER
:
274 if r
== pyf
.FP_VERIFY_RETRY_REMOVE_FINGER
:
278 def load_print_from_disk(self
, finger
):
280 Load a stored fingerprint from the users home directory.
282 - finger should be a value from Fingers.
287 raise "Device not open"
288 (r
, print_ptr
) = pyf
.fp_print_data_load(self
.dev
, finger
)
290 raise "could not load print from disk"
291 return Fprint(data_ptr
= print_ptr
)
293 def delete_stored_finger(self
, finger
):
295 Delete a fingerprint stored in the users home directory
297 - finger should be a value from Fingers.
300 raise "Device not open"
301 r
= pyf
.fp_print_data_delete(self
.dev
, finger
)
303 raise "delete failed"
305 class Minutia(pyf
.fp_minutia
):
306 """A single point of interest in a fingerprint."""
307 def __init__(self
, minutia_ptr
, img
):
309 self
.ptr
= minutia_ptr
310 pyf
.fp_minutia
.__init
__(self
, minutia_ptr
)
313 """An image returned from the fingerprint reader."""
314 def __init__(self
, img_ptr
, bin
= False):
315 """Private method."""
319 self
._minutiae
= None
323 pyf
.fp_img_free(self
._img
)
326 """The height of the image in pixels."""
327 return pyf
.fp_img_get_height(self
._img
)
329 """The width of the image in pixels."""
330 return pyf
.fp_img_get_width(self
._img
)
334 Return a string containing one byte per pixel, representing a grayscale image.
336 return pyf
.pyfp_img_get_data(self
._img
)
340 Return a string containing three bytes per pixel, representing a gray RGB image.
342 return pyf
.pyfp_img_get_rgb_data(self
._img
)
344 def save_to_file(self
, path
):
345 r
= pyf
.fp_img_save_to_file(self
._img
, path
)
349 def standardize(self
):
350 pyf
.fp_img_standardize(self
._img
)
358 i
= pyf
.fp_img_binarize(self
._img
)
360 raise "Binarize failed"
361 return Image(img_ptr
= i
, bin
= True)
365 return self
._minutiae
367 raise "Cannot find minutiae in binarized image"
370 (min_list
, nr
) = pyf
.fp_img_get_minutiae(self
._img
)
373 l
.append(Minutia(img
= self
, minutia_ptr
= pyf
.pyfp_deref_minutiae(min_list
, n
)))
378 """Provides access to some information about a libfprint driver."""
379 def __init__(self
, swig_drv_ptr
):
381 self
.drv
= swig_drv_ptr
388 """Return the driver name."""
389 return pyf
.fp_driver_get_name(self
.drv
)
392 """A longer, more desciptive version of the driver name."""
393 return pyf
.fp_driver_get_full_name(self
.drv
)
396 """Return an integer uniqly identifying the driver."""
397 return pyf
.fp_driver_get_driver_id(self
.drv
)
400 def __init__(self
, serial_data
= None, data_ptr
= None, dscv_ptr
= None, DscvList
= None):
402 The only parameter that should be used is serial_data, which
403 should be data previously aquired from data(), in the form of a string.
404 All other parameters are for internal use only.
406 # data_ptr is a SWIG pointer to a struct pf_print_data
407 # dscv_ptr is a SWIG pointer to a struct pf_dscv_print
408 # DscvList is a class instance used to free the allocated pf_dscv_print's
409 # with pf_dscv_prints_free when they're all unused.
410 # serial_data is a string as returned by data()
412 self
.data_ptr
= data_ptr
413 self
.dscv_ptr
= dscv_ptr
414 self
.DscvList
= DscvList
417 self
.data_ptr
= pyf
.fp_print_data_from_data(serial_data
)
420 if dscv_ptr
!= None and DscvList
== None:
421 raise "Programming error: Fprint constructed with dscv_prt with DscvList == None"
425 pyf
.fp_print_data_free(self
.data_ptr
)
426 # The dscv_ptr is freed when all the dscv prints have been garbage collected
428 def _get_print_data_ptr(self
):
429 if not self
.data_ptr
:
430 self
._data
_from
_dscv
()
434 """Return an integer identifing the driver used to scan this print."""
436 return pyf
.fp_print_data_get_driver_id(self
.data_ptr
)
438 return pyf
.fp_dscv_print_get_driver_id(self
.dscv_ptr
)
442 """Return an integer representing the type of device used to scan this print."""
444 return pyf
.fp_print_data_get_devtype(self
.data_ptr
)
446 return pyf
.fp_dscv_print_get_devtype(self
.dscv_ptr
)
451 If the Fprint was returned from discover_prints(), return
452 the Finger the Fprint represents. Otherwise raise an exception.
454 if not self
.dscv_ptr
:
455 raise "finger() needs a discovered print"
456 return pyf
.fp_dscv_print_get_finger(self
.dscv_ptr
)
458 def delete_from_disk(self
):
460 If the Fprint was returned from discover_prints(), delete it
461 from the users home directory. Otherwise raise an exception.
463 if not self
.dscv_ptr
:
464 raise "delete needs a discovered print"
465 return pyf
.fp_dscv_print_delete(self
.dscv_ptr
)
467 def save_to_disk(self
, finger
):
468 """Save the print to the users home directory.
470 - finger is a member of Fingers, indicating which
473 r
= pyf
.fp_print_data_save(self
.data_ptr
, finger
)
477 def _data_from_dscv(self
):
480 if not self
.dscv_ptr
:
482 (r
, ptr
) = pyf
.fp_print_data_from_dscv_print(self
.dscv_ptr
)
484 raise "print data from dscv failed"
489 Return a serialized dataset representing the fprint.
490 This data could be stored away, and later passed to the
491 contructor of Fprint.
493 if not self
.data_ptr
:
495 s
= pyf
.pyfp_print_get_data(self
.data_ptr
)
497 raise "serialization failed"
500 class DiscoveredPrints(list):
502 A list of stored fingerprints available from the users
505 def __init__(self
, dscv_devs_list
):
506 self
.ptr
= dscv_devs_list
509 x
= pyf
.pyfp_deref_dscv_print_ptr(dscv_devs_list
, i
)
512 self
.append(Fprint(dscv_ptr
= x
, DscvList
= self
))
515 pyf
.pf_dscv_prints_free(self
.ptr
)
517 def discover_prints():
518 """Look for fingerprints in the users home directory ;)"""
522 prints
= pyf
.fp_discover_prints()
525 print "Print discovery failed"
526 return DiscoveredPrints(prints
)
529 class DiscoveredDevices(list):
530 """A list of available devices."""
531 def __init__(self
, dscv_devs_list
):
532 self
.swig_list_ptr
= dscv_devs_list
535 x
= pyf
.pyfp_deref_dscv_dev_ptr(dscv_devs_list
, i
)
538 self
.append(Device(dscv_ptr
= x
, DscvList
= self
))
542 pyf
.fp_dscv_devs_free(self
.swig_list_ptr
)
544 def find_compatible(self
, fprint
):
546 Return a Device that is compatible with the fprint,
547 or None if no compatible device is found.
550 if n
.is_compatible(fprint
):
554 def discover_devices():
555 """Return a list of available devices."""
559 devs
= pyf
.fp_discover_devs()
562 raise "Device discovery failed"
563 return DiscoveredDevices(devs
)