diff --git a/TODO b/TODO index a137e92..62d210b 100644 --- a/TODO +++ b/TODO @@ -22,5 +22,4 @@ IMAGING aes4000 doesn't work very well, maybe due to small minutia count? PPMM parameter to get_minutiae seems to have no effect nbis minutiae should be stored in endian-independent format -return images with standard enroll/verify call variants diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 41b1a07..3548e66 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -249,7 +249,7 @@ EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. diff --git a/examples/enroll.c b/examples/enroll.c index 97671a6..2031938 100644 --- a/examples/enroll.c +++ b/examples/enroll.c @@ -47,13 +47,22 @@ struct fp_print_data *enroll(struct fp_dev *dev) { "complete the process.\n", fp_dev_get_nr_enroll_stages(dev)); do { + struct fp_img *img = NULL; + sleep(1); printf("\nScan your finger now.\n"); - r = fp_enroll_finger(dev, &enrolled_print); + + r = fp_enroll_finger_img(dev, &enrolled_print, &img); + if (img) { + fp_img_save_to_file(img, "enrolled.pgm"); + printf("Wrote scanned image to enrolled.pgm\n"); + fp_img_free(img); + } if (r < 0) { printf("Enroll failed with error %d\n", r); return NULL; } + switch (r) { case FP_ENROLL_COMPLETE: printf("Enroll complete!\n"); diff --git a/examples/verify.c b/examples/verify.c index ef66d5e..af4327f 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -44,9 +44,16 @@ int verify(struct fp_dev *dev, struct fp_print_data *data) int r; do { + struct fp_img *img = NULL; + sleep(1); printf("\nScan your finger now.\n"); - r = fp_verify_finger(dev, data); + r = fp_verify_finger_img(dev, data, &img); + if (img) { + fp_img_save_to_file(img, "verify.pgm"); + printf("Wrote scanned image to verify.pgm\n"); + fp_img_free(img); + } if (r < 0) { printf("verification failed with error %d :(\n", r); return r; diff --git a/libfprint/core.c b/libfprint/core.c index effded9..0aa6dd1 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -143,7 +143,7 @@ * match. libfprint does offer you some "is this print compatible?" helper * functions, so you don't have to worry about these details too much. * - * \section Synchronity/asynchronity + * \section sync Synchronity/asynchronity * * Currently, all data acquisition operations are synchronous and can * potentially block for extended periods of time. For example, the enroll @@ -850,16 +850,25 @@ API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev) * resultant enrollment data. The print_data parameter will not be modified * during any other enrollment stages, hence it is actually legal to pass NULL * as this argument for all but the final stage. + * + * If the device is an imaging device, it can also return the image from + * the scan, even when the enroll fails with a RETRY or FAIL code. It is legal + * to call this function even on non-imaging devices, just don't expect them to + * provide images. * * \param dev the device * \param print_data a location to return the resultant enrollment data from * the final stage. Must be freed with fp_print_data_free() after use. - * \return negative code on error, otherwise a code from #fp_verify_result + * \param img location to store the scan image. accepts NULL for no image + * storage. If an image is returned, it must be freed with fp_img_free() after + * use. + * \return negative code on error, otherwise a code from #fp_enroll_result */ -API_EXPORTED int fp_enroll_finger(struct fp_dev *dev, - struct fp_print_data **print_data) +API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev, + struct fp_print_data **print_data, struct fp_img **img) { struct fp_driver *drv = dev->drv; + struct fp_img *_img = NULL; int ret; int stage = dev->__enroll_stage; gboolean initial = FALSE; @@ -884,12 +893,18 @@ API_EXPORTED int fp_enroll_finger(struct fp_dev *dev, fp_dbg("%s will handle enroll stage %d/%d%s", drv->name, stage, dev->nr_enroll_stages - 1, initial ? " (initial)" : ""); - ret = drv->enroll(dev, initial, stage, print_data); + ret = drv->enroll(dev, initial, stage, print_data, &_img); if (ret < 0) { fp_err("enroll failed with code %d", ret); dev->__enroll_stage = -1; return ret; } + + if (img) + *img = _img; + else + fp_img_free(_img); + switch (ret) { case FP_ENROLL_PASS: fp_dbg("enroll stage passed"); @@ -925,15 +940,24 @@ API_EXPORTED int fp_enroll_finger(struct fp_dev *dev, /** \ingroup dev * Performs a new scan and verify it against a previously enrolled print. + * If the device is an imaging device, it can also return the image from + * the scan, even when the verify fails with a RETRY code. It is legal to + * call this function even on non-imaging devices, just don't expect them to + * provide images. + * * \param dev the device to perform the scan. * \param enrolled_print the print to verify against. Must have been previously * enrolled with a device compatible to the device selected to perform the scan. + * \param img location to store the scan image. accepts NULL for no image + * storage. If an image is returned, it must be freed with fp_img_free() after + * use. * \return negative code on error, otherwise a code from #fp_verify_result */ -API_EXPORTED int fp_verify_finger(struct fp_dev *dev, - struct fp_print_data *enrolled_print) +API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev, + struct fp_print_data *enrolled_print, struct fp_img **img) { struct fp_driver *drv = dev->drv; + struct fp_img *_img = NULL; int r; if (!enrolled_print) { @@ -952,12 +976,17 @@ API_EXPORTED int fp_verify_finger(struct fp_dev *dev, } fp_dbg("to be handled by %s", drv->name); - r = drv->verify(dev, enrolled_print); + r = drv->verify(dev, enrolled_print, &_img); if (r < 0) { fp_dbg("verify error %d", r); return r; } + if (img) + *img = _img; + else + fp_img_free(_img); + switch (r) { case FP_VERIFY_NO_MATCH: fp_dbg("result: no match"); diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index 350fe48..8b8ce08 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -557,7 +557,7 @@ static const unsigned char scan_comp[] = { static const unsigned char poll_data[] = { 0x30, 0x01 }; static int enroll(struct fp_dev *dev, gboolean initial, - int stage, struct fp_print_data **_data) + int stage, struct fp_print_data **_data, struct fp_img **img) { unsigned char *data; size_t data_len; @@ -692,7 +692,8 @@ static const unsigned char verify_hdr[] = { 0x00 }; -static int verify(struct fp_dev *dev, struct fp_print_data *print) +static int verify(struct fp_dev *dev, struct fp_print_data *print, + struct fp_img **img) { size_t data_len = sizeof(verify_hdr) + print->length; unsigned char *data; diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 435ec46..00add02 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -112,8 +112,9 @@ struct fp_driver { int (*init)(struct fp_dev *dev, unsigned long driver_data); void (*exit)(struct fp_dev *dev); int (*enroll)(struct fp_dev *dev, gboolean initial, int stage, - struct fp_print_data **print_data); - int (*verify)(struct fp_dev *dev, struct fp_print_data *data); + struct fp_print_data **print_data, struct fp_img **img); + int (*verify)(struct fp_dev *dev, struct fp_print_data *data, + struct fp_img **img); }; enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv); diff --git a/libfprint/fprint.h b/libfprint/fprint.h index 399b5e1..334fe99 100644 --- a/libfprint/fprint.h +++ b/libfprint/fprint.h @@ -128,7 +128,25 @@ enum fp_enroll_result { FP_ENROLL_RETRY_REMOVE_FINGER, }; -int fp_enroll_finger(struct fp_dev *dev, struct fp_print_data **print_data); +int fp_enroll_finger_img(struct fp_dev *dev, struct fp_print_data **print_data, + struct fp_img **img); + +/** \ingroup dev + * Performs an enroll stage. See \ref enrolling for an explanation of enroll + * stages. This function is just a shortcut to calling fp_enroll_finger_img() + * with a NULL image parameter. Be sure to read the description of + * fp_enroll_finger_img() in order to understand its behaviour. + * + * \param dev the device + * \param print_data a location to return the resultant enrollment data from + * the final stage. Must be freed with fp_print_data_free() after use. + * \return negative code on error, otherwise a code from #fp_enroll_result + */ +static inline int fp_enroll_finger(struct fp_dev *dev, + struct fp_print_data **print_data) +{ + return fp_enroll_finger_img(dev, print_data, NULL); +} /** \ingroup dev * Verification result codes returned from fp_verify_finger(). @@ -158,7 +176,22 @@ enum fp_verify_result { FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER, }; -int fp_verify_finger(struct fp_dev *dev, struct fp_print_data *enrolled_print); +int fp_verify_finger_img(struct fp_dev *dev, + struct fp_print_data *enrolled_print, struct fp_img **img); + +/** \ingroup dev + * Performs a new scan and verify it against a previously enrolled print. + * \param dev the device to perform the scan. + * \param enrolled_print the print to verify against. Must have been previously + * enrolled with a device compatible to the device selected to perform the scan. + * \return negative code on error, otherwise a code from #fp_verify_result + * \sa fp_verify_finger_img() + */ +static inline int fp_verify_finger(struct fp_dev *dev, + struct fp_print_data *enrolled_print) +{ + return fp_verify_finger_img(dev, enrolled_print, NULL); +} /* Data handling */ int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger, diff --git a/libfprint/img.c b/libfprint/img.c index 72504ae..9bc9a51 100644 --- a/libfprint/img.c +++ b/libfprint/img.c @@ -86,7 +86,7 @@ struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize) /** \ingroup img * Frees an image. Must be called when you are finished working with an image. - * \param img the image to destroy + * \param img the image to destroy. If NULL, function simply returns. */ API_EXPORTED void fp_img_free(struct fp_img *img) { diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index d8d651a..b9da4b5 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -157,7 +157,7 @@ err: #define MIN_ACCEPTABLE_MINUTIAE 10 int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage, - struct fp_print_data **ret) + struct fp_print_data **ret, struct fp_img **_img) { struct fp_img *img; struct fp_img_dev *imgdev = dev->priv; @@ -168,12 +168,17 @@ int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage, * use NFIQ to pick the best one, and discard the others */ r = fpi_imgdev_capture(imgdev, 0, &img); + + /* If we got an image, standardize it and return it even if the scan + * quality was too low for processing. */ + if (img) + fp_img_standardize(img); + if (_img) + *_img = img; if (r) return r; - fp_img_standardize(img); r = fpi_img_detect_minutiae(imgdev, img, &print); - fp_img_free(img); if (r < 0) return r; if (r < MIN_ACCEPTABLE_MINUTIAE) { @@ -189,22 +194,27 @@ int img_dev_enroll(struct fp_dev *dev, gboolean initial, int stage, #define BOZORTH3_DEFAULT_THRESHOLD 40 static int img_dev_verify(struct fp_dev *dev, - struct fp_print_data *enrolled_print) + struct fp_print_data *enrolled_print, struct fp_img **_img) { struct fp_img_dev *imgdev = dev->priv; struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv); - struct fp_img *img; + struct fp_img *img = NULL; struct fp_print_data *print; int match_score = imgdrv->bz3_threshold; int r; r = fpi_imgdev_capture(imgdev, 0, &img); + + /* If we got an image, standardize it and return it even if the scan + * quality was too low for processing. */ + if (img) + fp_img_standardize(img); + if (_img) + *_img = img; if (r) return r; - fp_img_standardize(img); r = fpi_img_detect_minutiae(imgdev, img, &print); - fp_img_free(img); if (r < 0) return r; if (r < MIN_ACCEPTABLE_MINUTIAE) {