API documentation

This commit is contained in:
Daniel Drake 2007-11-01 23:11:29 +00:00
parent 7e6f25908b
commit 2e6c3b940c
9 changed files with 2038 additions and 16 deletions

View file

@ -1,6 +1,6 @@
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
SUBDIRS = libfprint SUBDIRS = libfprint doc
if BUILD_EXAMPLES if BUILD_EXAMPLES
SUBDIRS += examples SUBDIRS += examples

2
TODO
View file

@ -7,6 +7,8 @@ make library optionally asynchronous and maybe thread-safe
nbis cleanups nbis cleanups
track open devices, so we can close them during libfprint close track open devices, so we can close them during libfprint close
free memory during libfprint close free memory during libfprint close
API function to determine if img device supports uncond. capture
race-free way of saying "save this print but don't overwrite"
NEW DRIVERS NEW DRIVERS
=========== ===========

View file

@ -85,6 +85,6 @@ AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default vis
AM_CFLAGS="-Werror-implicit-function-declaration -Wimplicit-int -Wunreachable-code -Wunused-function -Wunused-label -Wunused-value -Wunused-variable -Wnonnull -Wreturn-type -Wextra -Wshadow" AM_CFLAGS="-Werror-implicit-function-declaration -Wimplicit-int -Wunreachable-code -Wunused-function -Wunused-label -Wunused-value -Wunused-variable -Wnonnull -Wreturn-type -Wextra -Wshadow"
AC_SUBST(AM_CFLAGS) AC_SUBST(AM_CFLAGS)
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile]) AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT AC_OUTPUT

10
doc/Makefile.am Normal file
View file

@ -0,0 +1,10 @@
EXTRA_DIST = doxygen.cfg
docs: doxygen.cfg
doxygen $^
docs-upload: docs
ln -s html fprint-api
ncftpput -f ~/.ncftp/reactivated -m -R httpdocs fprint-api/
rm -f fprint-api

1294
doc/doxygen.cfg Normal file

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,256 @@
#include "fp_internal.h" #include "fp_internal.h"
/**
* \mainpage libfprint API Reference
* libfprint is an open source library to provide access to fingerprint
* scanning devices. For more info, see the
* <a href="http://www.reactivated.net/fprint/Libfprint">libfprint project
* homepage</a>.
*
* This documentation is aimed at application developers who wish to integrate
* fingerprint-related functionality into their software. libfprint has been
* designed so that you only have to do this once - by integrating your
* software with libfprint, you'll be supporting all the fingerprint readers
* that we have got our hands on. As such, the API is rather general (and
* therefore hopefully easy to comprehend!), and does it's best to hide the
* technical details that required to operate the hardware.
*
* This documentation is not aimed at developers wishing to develop and
* contribute fingerprint device drivers to libfprint.
*
* Feedback on this API and it's associated documentation is appreciated. Was
* anything unclear? Does anything seem unreasonably complicated? Is anything
* missing? Let us know on the
* <a href="http://www.reactivated.net/fprint/Mailing_list">mailing list</a>.
*
* \section enrollment Enrollment
*
* Before you dive into the API, it's worth introducing a couple of concepts.
*
* The process of enrolling a finger is where you effectively scan your
* finger for the purposes of teaching the system what your finger looks like.
* This means that you scan your fingerprint, then the system processes it and
* stores some data about your fingerprint to refer to later.
*
* \section verification Verification
*
* Verification is what most people think of when they think about fingerprint
* scanning. The process of verification is effectively performing a fresh
* fingerprint scan, and then comparing that scan to a finger that was
* previously enrolled.
*
* As an example scenario, verification can be used to implement what people
* would picture as fingerprint login (i.e. fingerprint replaces password).
* For example:
* - I enroll my fingerprint through some software that trusts I am who I say
* I am. This is a prerequisite before I can perform fingerprint-based
* login for my account.
* - Some time later, I want to login to my computer. I enter my username,
* but instead of prompting me for a password, it asks me to scan my finger.
* I scan my finger.
* - The system compares the finger I just scanned to the one that was
* enrolled earlier. If the system decides that the fingerprints match,
* I am successfully logged in. Otherwise, the system informs me that I am
* not authorised to login as that user.
*
* \section identification Identification
*
* libfprint supports enrollment and verification as described above. Although
* libfprint does not yet support identification (it is planned), it is worth
* introducing the concept to give you a complete picture.
*
* Identification is the process of comparing a freshly scanned fingerprint
* to a <em>collection</em> of previously enrolled fingerprints. For example,
* imagine there are 100 people in an organisation, and they all have enrolled
* their fingerprints. One user walks up to a fingerprint scanner and scans
* their finger. With <em>no other knowledge</em> of who that user might be,
* the system examines their fingerprint, looks in the database, and determines
* that the user is user number #61.
*
* In other words, verification might be seen as a one-to-one fingerprint
* comparison where you know the identity of the user that you wish to
* authenticate, whereas identification is a one-to-many comparison where you
* do not know the identity of the user that you wish to authenticate.
*
* \section compat_general Device and print compatibility
* Moving off generic conceptual ideas and onto libfprint-specific
* implementation details, here are some introductory notes regarding how
* libfprint copes with compatibility of fingerprints.
*
* libfprint deals with a whole variety of different fingerprint readers and
* the design includes considerations of compatibility and interoperability
* between multiple devices. Your application should also be prepared to
* work with more than one type of fingerprint reader and should consider that
* enrolled fingerprint X may not be compatible with the device the user has
* plugged in today.
*
* libfprint implements the principle that fingerprints from different devices
* are not necessarily compatible. For example, different devices may see
* significantly different areas of fingerprint surface, and comparing images
* between the devices would be unreliable. Also, devices can stretch and
* distort images in different ways.
*
* libfprint also implements the principle that in some cases, fingerprints
* <em>are</em> compatible between different devices. If you go and buy two
* identical fingerprint readers, it seems logical that you should be able
* to enroll on one and verify on another without problems.
*
* libfprint takes a fairly simplistic approach to these issues. Internally,
* fingerprint hardware is driven by individual drivers. libfprint enforces
* that a fingerprint that came from a device backed by driver X is never
* compared to a fingerprint that came from a device backed by driver Y.
*
* Additionally, libfprint is designed for the situation where a single driver
* may support a range of devices which differ in imaging or scanning
* properties. For example, a driver may support two ranges of devices which
* even though are programmed over the same interface, one device sees
* substantially less of the finger flesh, therefore images from the two
* device types should be incompatible despite being from the same driver. To
* implement this, each driver assigns a <em>device type</em> to each device
* that it detects based on its imaging characteristics. libfprint ensures that
* two prints being compared have the same device type.
*
* In summary, libfprint represents fingerprints in several internal structures
* and each representation will offer you a way of determining the
* \ref driver_id "driver ID" and \ref devtype "devtype" of the print in
* question. Prints are only compatible if the driver ID <b>and</b> devtypes
* 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
*
* Currently, all data acquisition operations are synchronous and can
* potentially block for extended periods of time. For example, the enroll
* function will block for an unpredictable amount of time until the user
* scans their finger.
*
* Alternative asynchronous/non-blocking functionality will be offered in
* future but has not been implemented yet.
*
* \section getting_started Getting started
*
* libfprint includes several simple functional examples under the examples/
* directory in the libfprint source distribution. Those are good starting
* points.
*
* Usually the first thing you want to do is determine which fingerprint
* devices are present. This is done through \ref dscv_dev "device discovery".
*
* Once you have found a device you would like to operate, you should open it.
* Refer to \ref dev "device operations". This section also details enrollment,
* image capture, and verification.
*
*
* That should be enough to get you started, but do remember there are
* documentation pages on other aspects of libfprint's API (see the modules
* page).
*/
/** @defgroup core Core library operations */
/**
* @defgroup dev Device operations
* In order to interact with fingerprint scanners, your software will
* interface primarily with libfprint's representation of devices, detailed
* on this page.
*
* \section enrolling Enrolling
* Enrolling is represented within libfprint as a multi-stage process. This
* slightly complicates things for application developers, but is required
* for a smooth process.
*
* Some devices require the user to scan their finger multiple times in
* order to complete the enrollment process. libfprint must return control
* to your application inbetween each scan in order for your application to
* instruct the user to swipe their finger again. Each scan is referred to
* as a stage, so a device that requires 3 scans for enrollment corresponds
* to you running 3 enrollment stages using libfprint.
*
* The fp_dev_get_nr_enroll_stages() function can be used to find out how
* many enroll stages are needed.
*
* In order to complete an enroll stage, you call an enroll function such
* as fp_enroll_finger(). The return of this function does not necessarily
* indicate that a stage has completed though, as the user may not have
* produced a good enough scan. Each stage may have to be retried several
* times.
*
* The exact semantics of the enroll functions are described in the
* fp_enroll_finger() documentation. You should pay careful attention to the
* details.
*
* \section imaging Imaging
* libfprint provides you with some ways to retrieve images of scanned
* fingers, such as the fp_dev_img_capture() function, or some enroll/verify
* function variants which provide images. You may wish to do something with
* such images in your application.
*
* However, you must be aware that not all hardware supported by libfprint
* operates like this. Most hardware does operate simply by sending
* fingerprint images to the host computer for further processing, but some
* devices do all fingerprint processing in hardware and do not present images
* to the host computer.
*
* You can use fp_dev_supports_imaging() to see if image capture is possible
* on a particular device. Your application must be able to cope with the
* fact that libfprint does support regular operations (e.g. enrolling and
* verification) on some devices which do not provide images.
*
* \section devtype Devtypes
* Internally, the \ref drv "driver" behind a device assigns a 32-bit
* <em>devtype</em> identifier to the device. This cannot be used as a unique
* ID for a specific device as many devices under the same range may share
* the same devtype. The devtype may even be 0 in all cases.
*
* The only reason you may be interested in retrieving the devtype for a
* device is for the purpose of checking if some print data is compatible
* with a device. libfprint uses the devtype as one way of checking that the
* print you are verifying is compatible with the device in question - the
* devtypes must be equal. This effectively allows drivers to support more
* than one type of device where the data from each one is not compatible with
* the other. Note that libfprint does provide you with helper functions to
* determine whether a print is compatible with a device, so under most
* circumstances, you don't have to worry about devtypes at all.
*/
/** @defgroup dscv_dev Device discovery
* These functions allow you to scan the system for supported fingerprint
* scanning hardware. This is your starting point when integrating libfprint
* into your software.
*
* When you've identified a discovered device that you would like to control,
* you can open it with fp_dev_open(). Note that discovered devices may no
* longer be available at the time when you want to open them, for example
* the user may have unplugged the device.
*/
/** @defgroup drv Driver operations
* Internally, libfprint is abstracted into various drivers to communicate
* with the different types of supported fingerprint readers. libfprint works
* hard so that you don't have to care about these internal abstractions,
* however there are some situations where you may be interested in a little
* behind-the-scenes driver info.
*
* You can obtain the driver for a device using fp_dev_get_driver(), which
* you can pass to the functions documented on this page.
*
* \section driver_id Driver IDs
* Each driver is assigned a unique ID by the project maintainer. These
* assignments are
* <a href="http://www.reactivated.net/fprint/Driver_ID_assignments">
* documented on the wiki</a> and will never change.
*
* The only reason you may be interested in retrieving the driver ID for a
* driver is for the purpose of checking if some print data is compatible
* with a device. libfprint uses the driver ID as one way of checking that
* the print you are trying to verify is compatible with the device in
* question - it ensures that enrollment data from one driver is never fed to
* another. Note that libfprint does provide you with helper functions to
* determine whether a print is compatible with a device, so under most
* circumstances, you don't have to worry about driver IDs at all.
*/
static GList *registered_drivers = NULL; static GList *registered_drivers = NULL;
void fpi_log(enum fpi_log_level level, const char *component, void fpi_log(enum fpi_log_level level, const char *component,
@ -147,6 +397,12 @@ static struct fp_dscv_dev *discover_dev(struct usb_device *udev)
return ddev; return ddev;
} }
/** \ingroup dscv_dev
* Scans the system and returns a list of discovered devices. This is your
* entry point into finding a fingerprint reader to operate.
* \returns a NULL-terminated list of discovered devices. Must be freed with
* fp_dscv_devs_free() after use.
*/
API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void) API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
{ {
GList *tmplist = NULL; GList *tmplist = NULL;
@ -191,6 +447,12 @@ API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
return list; return list;
} }
/** \ingroup dscv_dev
* Free a list of discovered devices. This function destroys the list and all
* discovered devices that it included, so make sure you have opened your
* discovered device <b>before</b> freeing the list.
* \param devs the list of discovered devices
*/
API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs) API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
{ {
int i; int i;
@ -202,11 +464,21 @@ API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
g_free(devs); g_free(devs);
} }
/** \ingroup dscv_dev
* Gets the \ref drv "driver" for a discovered device.
* \param dev the discovered device
* \returns the driver backing the device
*/
API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev) API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev)
{ {
return dev->drv; return dev->drv;
} }
/** \ingroup dscv_dev
* Gets the \ref devtype "devtype" for a discovered device.
* \param dev the discovered device
* \returns the devtype of the device
*/
API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev) API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev)
{ {
return dev->devtype; return dev->devtype;
@ -225,6 +497,13 @@ enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv)
} }
} }
/** \ingroup dscv_dev
* Determines if a specific \ref print_data "stored print" appears to be
* compatible with a discovered device.
* \param dev the discovered device
* \param data the print for compatibility checking
* \returns 1 if the print is compatible with the device, 0 otherwise
*/
API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev, API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
struct fp_print_data *data) struct fp_print_data *data)
{ {
@ -233,6 +512,13 @@ API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
data->type); data->type);
} }
/** \ingroup dscv_dev
* Determines if a specific \ref dscv_print "discovered print" appears to be
* compatible with a discovered device.
* \param dev the discovered device
* \param data the discovered print for compatibility checking
* \returns 1 if the print is compatible with the device, 0 otherwise
*/
API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev, API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
struct fp_dscv_print *data) struct fp_dscv_print *data)
{ {
@ -240,6 +526,14 @@ API_EXPORTED int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
data->driver_id, data->devtype, 0); data->driver_id, data->devtype, 0);
} }
/** \ingroup dscv_dev
* Searches a list of discovered devices for a device that appears to be
* compatible with a \ref print_data "stored print".
* \param devs a list of discovered devices
* \param data the print under inspection
* \returns the first discovered device that appears to support the print, or
* NULL if no apparently compatible devices could be found
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs, API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs,
struct fp_print_data *data) struct fp_print_data *data)
{ {
@ -252,6 +546,14 @@ API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev *
return NULL; return NULL;
} }
/** \ingroup dscv_dev
* Searches a list of discovered devices for a device that appears to be
* compatible with a \ref dscv_print "discovered print".
* \param devs a list of discovered devices
* \param print the print under inspection
* \returns the first discovered device that appears to support the print, or
* NULL if no apparently compatible devices could be found
*/
API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs, API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs,
struct fp_dscv_print *print) struct fp_dscv_print *print)
{ {
@ -264,6 +566,13 @@ API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev *
return NULL; return NULL;
} }
/** \ingroup dev
* Opens and initialises a device. This is the function you call in order
* to convert a \ref dscv_dev "discovered device" into an actual device handle
* that you can perform operations with.
* \param ddev the discovered device to open
* \returns the opened device handle, or NULL on error
*/
API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev) API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
{ {
struct fp_dev *dev; struct fp_dev *dev;
@ -295,6 +604,11 @@ API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
return dev; return dev;
} }
/** \ingroup dev
* Close a device. You must call this function when you are finished using
* a fingerprint device.
* \param dev the device to close
*/
API_EXPORTED void fp_dev_close(struct fp_dev *dev) API_EXPORTED void fp_dev_close(struct fp_dev *dev)
{ {
fp_dbg(""); fp_dbg("");
@ -304,21 +618,43 @@ API_EXPORTED void fp_dev_close(struct fp_dev *dev)
g_free(dev); g_free(dev);
} }
/** \ingroup dev
* Get the \ref drv "driver" for a fingerprint device.
* \param dev the device
* \returns the driver controlling the device
*/
API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev) API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev)
{ {
return dev->drv; return dev->drv;
} }
/** \ingroup dev
* Gets the number of \ref enrolling "enroll stages" required to enroll a
* fingerprint with the device.
* \param dev the device
* \returns the number of enroll stages
*/
API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev) API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev)
{ {
return dev->nr_enroll_stages; return dev->nr_enroll_stages;
} }
/** \ingroup dev
* Gets the \ref devtype "devtype" for a device.
* \param dev the device
* \returns the devtype
*/
API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev) API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev)
{ {
return dev->devtype; return dev->devtype;
} }
/** \ingroup dev
* Determines if a stored print is compatible with a certain device.
* \param dev the device
* \param data the stored print
* \returns 1 if the print is compatible with the device, 0 if not
*/
API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev, API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
struct fp_print_data *data) struct fp_print_data *data)
{ {
@ -327,6 +663,13 @@ API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
data->type); data->type);
} }
/** \ingroup dev
* Determines if a \ref dscv_print "discovered print" appears to be compatible
* with a certain device.
* \param dev the device
* \param data the discovered print
* \returns 1 if the print is compatible with the device, 0 if not
*/
API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev, API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev,
struct fp_dscv_print *data) struct fp_dscv_print *data)
{ {
@ -334,16 +677,31 @@ API_EXPORTED int fp_dev_supports_dscv_print(struct fp_dev *dev,
0, data->driver_id, data->devtype, 0); 0, data->driver_id, data->devtype, 0);
} }
/** \ingroup drv
* Retrieves the name of the driver. For example: "upekts"
* \param drv the driver
* \returns the driver name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv) API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv)
{ {
return drv->name; return drv->name;
} }
/** \ingroup drv
* Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip"
* \param drv the driver
* \returns the descriptive name. Must not be modified or freed.
*/
API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv) API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
{ {
return drv->full_name; return drv->full_name;
} }
/** \ingroup drv
* Retrieves the driver ID code for a driver.
* \param drv the driver
* \returns the driver ID
*/
API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv) API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv)
{ {
return drv->id; return drv->id;
@ -356,11 +714,39 @@ static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
return dev->priv; return dev->priv;
} }
/** \ingroup dev
* Determines if a device has imaging capabilities. If a device has imaging
* capabilities you are able to perform imaging operations such as retrieving
* scan images using fp_dev_img_capture(). However, not all devices are
* imaging devices - some do all processing in hardware. This function will
* indicate which class a device in question falls into.
* \param dev the fingerprint device
* \returns 1 if the device is an imaging device, 0 if the device does not
* provide images to the host computer
*/
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev) API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
{ {
return dev->drv->type == DRIVER_IMAGING; return dev->drv->type == DRIVER_IMAGING;
} }
/** \ingroup dev
* Captures an \ref img "image" from a device. The returned image is the raw
* image provided by the device, you may wish to \ref img_std "standardize" it.
*
* If set, the <tt>unconditional</tt> flag indicates that the device should
* capture an image unconditionally, regardless of whether a finger is there
* or not. If unset, this function will block until a finger is detected on
* the sensor.
*
* \param dev the device
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
* \param image a location to return the captured image. Must be freed with
* fp_img_free() after use.
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
* unconditional flag was set but the device does not support this, or that the
* device does not support imaging.
* \sa fp_dev_supports_imaging()
*/
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional, API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **image) struct fp_img **image)
{ {
@ -373,6 +759,15 @@ API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
return fpi_imgdev_capture(imgdev, unconditional, image); return fpi_imgdev_capture(imgdev, unconditional, image);
} }
/** \ingroup dev
* Gets the expected width of images that will be captured from the device.
* This function will return -1 for devices that are not
* \ref imaging "imaging devices". If the width of images from this device
* can vary, 0 will be returned.
* \param dev the device
* \returns the expected image width, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev) API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
{ {
struct fp_img_dev *imgdev = dev_to_img_dev(dev); struct fp_img_dev *imgdev = dev_to_img_dev(dev);
@ -384,6 +779,15 @@ API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
return fpi_imgdev_get_img_width(imgdev); return fpi_imgdev_get_img_width(imgdev);
} }
/** \ingroup dev
* Gets the expected height of images that will be captured from the device.
* This function will return -1 for devices that are not
* \ref imaging "imaging devices". If the height of images from this device
* can vary, 0 will be returned.
* \param dev the device
* \returns the expected image height, or 0 for variable, or -1 for non-imaging
* devices.
*/
API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev) API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
{ {
struct fp_img_dev *imgdev = dev_to_img_dev(dev); struct fp_img_dev *imgdev = dev_to_img_dev(dev);
@ -395,6 +799,51 @@ API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
return fpi_imgdev_get_img_height(imgdev); return fpi_imgdev_get_img_height(imgdev);
} }
/** \ingroup dev
* Performs an enroll stage. See \ref enrolling for an explanation of enroll
* stages.
*
* If no enrollment is in process, this kicks of the process and runs the
* first stage. If an enrollment is already in progress, calling this
* function runs the next stage, which may well be the last.
*
* A negative error code may be returned from any stage. When this occurs,
* further calls to the enroll function will start a new enrollment process,
* i.e. a negative error code indicates that the enrollment process has been
* aborted. These error codes only ever indicate unexpected internal errors
* or I/O problems.
*
* The RETRY codes from #fp_enroll_result may be returned from any enroll
* stage. These codes indicate that the scan was not succesful in that the
* user did not position their finger correctly or similar. When a RETRY code
* is returned, the enrollment stage is <b>not</b> advanced, so the next call
* into this function will retry the current stage again. The current stage may
* need to be retried several times.
*
* The fp_enroll_result#FP_ENROLL_FAIL code may be returned from any enroll
* stage. This code indicates that even though the scans themselves have been
* acceptable, data processing applied to these scans produces incomprehensible
* results. In other words, the user may have been scanning a different finger
* for each stage or something like that. Like negative error codes, this
* return code indicates that the enrollment process has been aborted.
*
* The fp_enroll_result#FP_ENROLL_PASS code will only ever be returned for
* non-final stages. This return code indicates that the scan was acceptable
* and the next call into this function will advance onto the next enroll
* stage.
*
* The fp_enroll_result#FP_ENROLL_COMPLETE code will only ever be returned
* from the final enroll stage. It indicates that enrollment completed
* successfully, and that print_data has been assigned to point to the
* 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.
*
* \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
*/
API_EXPORTED int fp_enroll_finger(struct fp_dev *dev, API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
struct fp_print_data **print_data) struct fp_print_data **print_data)
{ {
@ -462,6 +911,13 @@ API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
return ret; return ret;
} }
/** \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
*/
API_EXPORTED int fp_verify_finger(struct fp_dev *dev, API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print) struct fp_print_data *enrolled_print)
{ {
@ -517,6 +973,11 @@ API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
return r; return r;
} }
/** \ingroup core
* Initialise libfprint. This function must be called before you attempt to
* use the library in any way.
* \return 0 on success, non-zero on error.
*/
API_EXPORTED int fp_init(void) API_EXPORTED int fp_init(void)
{ {
fp_dbg(""); fp_dbg("");
@ -525,4 +986,3 @@ API_EXPORTED int fp_init(void)
return 0; return 0;
} }

View file

@ -30,6 +30,21 @@
#define DIR_PERMS 0700 #define DIR_PERMS 0700
/** @defgroup print_data Stored prints
* Stored prints are represented by a structure named <tt>fp_print_data</tt>.
* Stored prints are originally obtained from an enrollment function such as
* fp_enroll_finger().
*
* This page documents the various operations you can do with a stored print.
* Note that by default, "stored prints" are not actually stored anywhere
* except in RAM. For the simple scenarios, libfprint provides a simple API
* for you to save and load the stored prints referring to a single user in
* their home directory. For more advanced users, libfprint provides APIs for
* you to convert print data to a byte string, and to reconstruct stored prints
* from such data at a later point. You are welcome to store these byte strings
* in any fashion that suits you.
*/
/* FIXME: should free this during library shutdown */ /* FIXME: should free this during library shutdown */
static char *base_store = NULL; static char *base_store = NULL;
@ -92,6 +107,15 @@ struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
fpi_driver_get_data_type(dev->drv), length); fpi_driver_get_data_type(dev->drv), length);
} }
/** \ingroup print_data
* Convert a stored print into a unified representation inside a data buffer.
* You can then store this data buffer in any way that suits you, and load
* it back at some later time using fp_print_data_from_data().
* \param data the stored print
* \param ret output location for the data buffer. Must be freed with free()
* after use.
* \returns the size of the freshly allocated buffer, or 0 on error.
*/
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data, API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
unsigned char **ret) unsigned char **ret)
{ {
@ -116,6 +140,15 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
return buflen; return buflen;
} }
/** \ingroup print_data
* Load a stored print from a data buffer. The contents of said buffer must
* be the untouched contents of a buffer previously supplied to you by the
* fp_print_data_get_data() function.
* \param buf the data buffer
* \param buflen the length of the buffer
* \returns the stored print represented by the data, or NULL on error. Must
* be freed with fp_print_data_free() after use.
*/
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf, API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
size_t buflen) size_t buflen)
{ {
@ -170,6 +203,21 @@ static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
return __get_path_to_print(dev->drv->id, dev->devtype, finger); return __get_path_to_print(dev->drv->id, dev->devtype, finger);
} }
/** \ingroup print_data
* Saves a stored print to disk, assigned to a specific finger. Even though
* you are limited to storing only the 10 human fingers, this is a
* per-device-type limit. For example, you can store the users right index
* finger from a DigitalPersona scanner, and you can also save the right index
* finger from a UPEK scanner. When you later come to load the print, the right
* one will be automatically selected.
*
* This function will unconditionally overwrite a fingerprint previously
* saved for the same finger and device type. The print is saved in a hidden
* directory beneath the current user's home directory.
* \param data the stored print to save to disk
* \param finger the finger that this print corresponds to
* \returns 0 on success, non-zero on error.
*/
API_EXPORTED int fp_print_data_save(struct fp_print_data *data, API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
enum fp_finger finger) enum fp_finger finger)
{ {
@ -242,6 +290,7 @@ static int load_from_file(char *path, struct fp_print_data **data)
gsize length; gsize length;
gchar *contents; gchar *contents;
GError *err = NULL; GError *err = NULL;
struct fp_print_data *fdata;
fp_dbg("from %s", path); fp_dbg("from %s", path);
g_file_get_contents(path, &contents, &length, &err); g_file_get_contents(path, &contents, &length, &err);
@ -256,11 +305,28 @@ static int load_from_file(char *path, struct fp_print_data **data)
return r; return r;
} }
*data = fp_print_data_from_data(contents, length); fdata = fp_print_data_from_data(contents, length);
g_free(contents); g_free(contents);
if (!fdata)
return -EIO;
*data = fdata;
return 0; return 0;
} }
/** \ingroup print_data
* Loads a previously stored print from disk. The print must have been saved
* earlier using the fp_print_data_save() function.
*
* A return code of -ENOENT indicates that the fingerprint requested could not
* be found. Other error codes (both positive and negative) are possible for
* obscure error conditions (e.g. corruption).
*
* \param dev the device you are loading the print for
* \param finger the finger of the file you are loading
* \param data output location to put the corresponding stored print. Must be
* freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error
*/
API_EXPORTED int fp_print_data_load(struct fp_dev *dev, API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data) enum fp_finger finger, struct fp_print_data **data)
{ {
@ -287,27 +353,89 @@ API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
return 0; return 0;
} }
/** \ingroup print_data
* Attempts to load a stored print based on a \ref dscv_print
* "discovered print" record.
*
* A return code of -ENOENT indicates that the file referred to by the
* discovered print could not be found. Other error codes (both positive and
* negative) are possible for obscure error conditions (e.g. corruption).
*
* \param print the discovered print
* \param data output location to point to the corresponding stored print. Must
* be freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error.
*/
API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print, API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
struct fp_print_data **data) struct fp_print_data **data)
{ {
return load_from_file(print->path, data); return load_from_file(print->path, data);
} }
/** \ingroup print_data
* Frees a stored print. Must be called when you are finished using the print.
* \param data the stored print to destroy
*/
API_EXPORTED void fp_print_data_free(struct fp_print_data *data) API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
{ {
g_free(data); g_free(data);
} }
/** \ingroup print_data
* Gets the \ref driver_id "driver ID" for a stored print. The driver ID
* indicates which driver the print originally came from. The print is
* only usable with a device controlled by that driver.
* \param data the stored print
* \returns the driver ID of the driver compatible with the print
*/
API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data) API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
{ {
return data->driver_id; return data->driver_id;
} }
/** \ingroup print_data
* Gets the \ref devtype "devtype" for a stored print. The devtype represents
* which type of device under the parent driver is compatible with the print.
* \param data the stored print
* \returns the devtype of the device range compatible with the print
*/
API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data) API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
{ {
return data->devtype; return data->devtype;
} }
/** @defgroup dscv_print Print discovery
* The \ref print_data "stored print" documentation detailed a simple API
* for storing per-device prints for a single user, namely
* fp_print_data_save(). It also detailed a load function,
* fp_print_data_load(), but usage of this function is limited to scenarios
* where you know which device you would like to use, and you know which
* finger you are looking to verify.
*
* In other cases, it would be more useful to be able to enumerate all
* previously saved prints, potentially even before device discovery. These
* functions are designed to offer this functionality to you.
*
* Discovered prints are stored in a <tt>dscv_print</tt> structure, and you
* can use functions documented below to access some information about these
* prints. You can determine if a discovered print appears to be compatible
* with a device using functions such as fp_dscv_dev_supports_dscv_print() and
* fp_dev_supports_dscv_print().
*
* When you are ready to use the print, you can load it into memory in the form
* of a stored print by using the fp_print_data_from_dscv_print() function.
*
* You may have noticed the use of the word "appears" in the above paragraphs.
* libfprint performs print discovery simply by examining the file and
* directory structure of libfprint's private data store. It does not examine
* the actual prints themselves. Just because a print has been discovered
* and appears to be compatible with a certain device does not necessarily mean
* that it is usable; when you come to load or use it, under unusual
* circumstances it may turn out that the print is corrupt or not for the
* device that it appeared to be. Also, it is possible that the print may have
* been deleted by the time you come to load it.
*/
static GSList *scan_dev_store_dir(char *devpath, uint16_t driver_id, static GSList *scan_dev_store_dir(char *devpath, uint16_t driver_id,
uint32_t devtype, GSList *list) uint32_t devtype, GSList *list)
{ {
@ -389,6 +517,12 @@ static GSList *scan_driver_store_dir(char *drvpath, uint16_t driver_id,
return list; return list;
} }
/** \ingroup dscv_print
* Scans the users home directory and returns a list of prints that were
* previously saved using fp_print_data_save().
* \returns a NULL-terminated list of discovered prints, must be freed with
* fp_dscv_prints_free() after use.
*/
API_EXPORTED struct fp_dscv_print **fp_discover_prints(void) API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
{ {
GDir *dir; GDir *dir;
@ -444,6 +578,12 @@ API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
return list; return list;
} }
/** \ingroup dscv_print
* Frees a list of discovered prints. This function also frees the discovered
* prints themselves, so make sure you do not use any discovered prints
* after calling this function.
* \param prints the list of discovered prints
*/
API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints) API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
{ {
int i; int i;
@ -460,17 +600,37 @@ API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
g_free(prints); g_free(prints);
} }
/** \ingroup dscv_print
* Gets the \ref driver_id "driver ID" for a discovered print. The driver ID
* indicates which driver the print originally came from. The print is only
* usable with a device controlled by that driver.
* \param print the discovered print
* \returns the driver ID of the driver compatible with the print
*/
API_EXPORTED uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print) API_EXPORTED uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print)
{ {
return print->driver_id; return print->driver_id;
} }
/** \ingroup dscv_print
* Gets the \ref devtype "devtype" for a discovered print. The devtype
* represents which type of device under the parent driver is compatible
* with the print.
* \param print the discovered print
* \returns the devtype of the device range compatible with the print
*/
API_EXPORTED uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print) API_EXPORTED uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print)
{ {
return print->devtype; return print->devtype;
} }
/** \ingroup dscv_print
* Gets the finger code for a discovered print.
* \param print discovered print
* \returns a finger code from #fp_finger
*/
API_EXPORTED enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print) API_EXPORTED enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print)
{ {
return print->finger; return print->finger;
} }

View file

@ -31,17 +31,23 @@ struct fp_print_data;
struct fp_img; struct fp_img;
/* misc/general stuff */ /* misc/general stuff */
/** \ingroup print_data
* Numeric codes used to refer to fingers (and thumbs) of a human. These are
* purposely not available as strings, to avoid getting the library tangled up
* in localization efforts.
*/
enum fp_finger { enum fp_finger {
LEFT_THUMB = 1, LEFT_THUMB = 1, /** thumb (left hand) */
LEFT_INDEX, LEFT_INDEX, /** index finger (left hand) */
LEFT_MIDDLE, LEFT_MIDDLE, /** middle finger (left hand) */
LEFT_RING, LEFT_RING, /** ring finger (left hand) */
LEFT_LITTLE, LEFT_LITTLE, /** little finger (left hand) */
RIGHT_THUMB, RIGHT_THUMB, /** thumb (right hand) */
RIGHT_INDEX, RIGHT_INDEX, /** index finger (right hand) */
RIGHT_MIDDLE, RIGHT_MIDDLE, /** middle finger (right hand) */
RIGHT_RING, RIGHT_RING, /** ring finger (right hand) */
RIGHT_LITTLE, RIGHT_LITTLE, /** little finger (right hand) */
}; };
/* Drivers */ /* Drivers */
@ -90,26 +96,65 @@ int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
int fp_dev_get_img_width(struct fp_dev *dev); int fp_dev_get_img_width(struct fp_dev *dev);
int fp_dev_get_img_height(struct fp_dev *dev); int fp_dev_get_img_height(struct fp_dev *dev);
/* Enrollment */ /** \ingroup dev
* Enrollment result codes returned from fp_enroll_finger().
* Result codes with RETRY in the name suggest that the scan failed due to
* user error. Applications will generally want to inform the user of the
* problem and then retry the enrollment stage. For more info on the semantics
* of interpreting these result codes and tracking enrollment process, see
* \ref enrolling.
*/
enum fp_enroll_result { enum fp_enroll_result {
/** Enrollment completed successfully, the enrollment data has been
* returned to the caller. */
FP_ENROLL_COMPLETE = 1, FP_ENROLL_COMPLETE = 1,
/** Enrollment failed due to incomprehensible data; this may occur when
* the user scans a different finger on each enroll stage. */
FP_ENROLL_FAIL, FP_ENROLL_FAIL,
/** Enroll stage passed; more stages are need to complete the process. */
FP_ENROLL_PASS, FP_ENROLL_PASS,
/** The enrollment scan did not succeed due to poor scan quality or
* other general user scanning problem. */
FP_ENROLL_RETRY = 100, FP_ENROLL_RETRY = 100,
/** The enrollment scan did not succeed because the finger swipe was
* too short. */
FP_ENROLL_RETRY_TOO_SHORT, FP_ENROLL_RETRY_TOO_SHORT,
/** The enrollment scan did not succeed because the finger was not
* centered on the scanner. */
FP_ENROLL_RETRY_CENTER_FINGER, FP_ENROLL_RETRY_CENTER_FINGER,
/** The verification scan did not succeed due to quality or pressure
* problems; the user should remove their finger from the scanner before
* retrying. */
FP_ENROLL_RETRY_REMOVE_FINGER, FP_ENROLL_RETRY_REMOVE_FINGER,
}; };
int fp_enroll_finger(struct fp_dev *dev, struct fp_print_data **print_data); int fp_enroll_finger(struct fp_dev *dev, struct fp_print_data **print_data);
/* Verification */ /** \ingroup dev
* Verification result codes returned from fp_verify_finger().
* Result codes with RETRY in the name suggest that the scan failed due to
* user error. Applications will generally want to inform the user of the
* problem and then retry the verify operation.
*/
enum fp_verify_result { enum fp_verify_result {
/** The verification scan completed successfully, but the newly scanned
* fingerprint does not match the fingerprint being verified against. */
FP_VERIFY_NO_MATCH = 0, FP_VERIFY_NO_MATCH = 0,
/** The verification scan completed successfully and the newly scanned
* fingerprint does match the fingerprint being verified. */
FP_VERIFY_MATCH = 1, FP_VERIFY_MATCH = 1,
/** The verification scan did not succeed due to poor scan quality or
* other general user scanning problem. */
FP_VERIFY_RETRY = FP_ENROLL_RETRY, FP_VERIFY_RETRY = FP_ENROLL_RETRY,
/** The verification scan did not succeed because the finger swipe was
* too short. */
FP_VERIFY_RETRY_TOO_SHORT = FP_ENROLL_RETRY_TOO_SHORT, FP_VERIFY_RETRY_TOO_SHORT = FP_ENROLL_RETRY_TOO_SHORT,
/** The verification scan did not succeed because the finger was not
* centered on the scanner. */
FP_VERIFY_RETRY_CENTER_FINGER = FP_ENROLL_RETRY_CENTER_FINGER, FP_VERIFY_RETRY_CENTER_FINGER = FP_ENROLL_RETRY_CENTER_FINGER,
/** The verification scan did not succeed due to quality or pressure
* problems; the user should remove their finger from the scanner before
* retrying. */
FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER, FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER,
}; };

View file

@ -28,6 +28,23 @@
#include "nbis/include/bozorth.h" #include "nbis/include/bozorth.h"
#include "nbis/include/lfs.h" #include "nbis/include/lfs.h"
/** @defgroup img Image operations
* libfprint offers several ways of retrieving images from imaging devices,
* one example being the fp_dev_img_capture() function. The functions
* documented below allow you to work with such images.
*
* \section img_fmt Image format
* All images are represented as 8-bit greyscale data.
*
* \section img_std Image standardization
* In some contexts, images you are provided through libfprint are raw images
* from the hardware. The orientation of these varies from device-to-device,
* as does the color scheme (black-on-white or white-on-black?). libfprint
* provides the fp_img_standardize function to convert images into standard
* form, which is defined to be: finger flesh as black on white surroundings,
* natural upright orientation.
*/
struct fp_img *fpi_img_new(size_t length) struct fp_img *fpi_img_new(size_t length)
{ {
struct fp_img *img = g_malloc(sizeof(*img) + length); struct fp_img *img = g_malloc(sizeof(*img) + length);
@ -66,26 +83,53 @@ struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize)
return g_realloc(img, sizeof(*img) + newsize); return g_realloc(img, sizeof(*img) + newsize);
} }
/** \ingroup img
* Frees an image. Must be called when you are finished working with an image.
* \param img the image to destroy
*/
API_EXPORTED void fp_img_free(struct fp_img *img) API_EXPORTED void fp_img_free(struct fp_img *img)
{ {
g_free(img); g_free(img);
} }
/** \ingroup img
* Gets the pixel height of an image.
* \param img an image
* \returns the height of the image
*/
API_EXPORTED int fp_img_get_height(struct fp_img *img) API_EXPORTED int fp_img_get_height(struct fp_img *img)
{ {
return img->height; return img->height;
} }
/** \ingroup img
* Gets the pixel width of an image.
* \param img an image
* \returns the width of the image
*/
API_EXPORTED int fp_img_get_width(struct fp_img *img) API_EXPORTED int fp_img_get_width(struct fp_img *img)
{ {
return img->width; return img->width;
} }
/** \ingroup img
* Gets the greyscale data for an image. This data must not be modified or
* freed, and must not be used after fp_img_free() has been called.
* \param img an image
* \returns a pointer to libfprint's internal data for the image
*/
API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img) API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img)
{ {
return img->data; return img->data;
} }
/** \ingroup img
* A quick convenience function to save an image to a file in
* <a href="http://netpbm.sourceforge.net/doc/pgm.html">PGM format</a>.
* \param img the image to save
* \param path the path to save the image. Existing files will be overwritten.
* \returns 0 on success, non-zero on error.
*/
API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path) API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path)
{ {
FILE *fd = fopen(path, "w"); FILE *fd = fopen(path, "w");
@ -159,6 +203,13 @@ static void invert_colors(struct fp_img *img)
img->data[i] = 0xff - img->data[i]; img->data[i] = 0xff - img->data[i];
} }
/** \ingroup img
* \ref img_std "Standardizes" an image by normalizing its orientation, colors,
* etc. It is safe to call this multiple times on an image, libfprint keeps
* track of the work it needs to do to make an image standard and will not
* perform these operations more than once for a given image.
* \param img the image to standardize
*/
API_EXPORTED void fp_img_standardize(struct fp_img *img) API_EXPORTED void fp_img_standardize(struct fp_img *img)
{ {
if (img->flags & FP_IMG_V_FLIPPED) { if (img->flags & FP_IMG_V_FLIPPED) {