Commit 5485f803 authored by Daniel Drake's avatar Daniel Drake

Return images through enroll/verify path

Added new API functions to obtain images, even when scans are bad, perhaps
a useful way to show the user just how good/bad the scan actually was.

Drivers and examples updated accordingly.
parent a86cd519
......@@ -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
......@@ -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.
......
......@@ -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");
......
......@@ -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;
......
......@@ -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");
......
......@@ -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;
......
......@@ -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);
......
......@@ -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,
......
......@@ -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)
{
......
......@@ -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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment