Usage: import pyfprint
Signed-off-by: Lukas Sandström <[EMAIL PROTECTED]>
---
Hi.
I've rebased the Python bindings I wrote some time ago.
New feature: *the build-system is working* (at least for me,
remember to pass --enable-python to ./configure)
You can test it using pyfprint_demo, available at
git://repo.or.cz/pyfprint_demo.git
The patch applies to the v0.0 libfprint branch. I'll look
into porting it to 1.0 sometime in this or the next week.
Patches and suggestions welcome.
/Lukas
Makefile.am | 6 +
autogen.sh | 4 +-
configure.ac | 16 ++-
m4/ac_pkg_swig.m4 | 122 +++++++++++++
m4/ac_python_devel.m4 | 265 ++++++++++++++++++++++++++++
m4/swig_python.m4 | 65 +++++++
pyfprint/Makefile.am | 12 ++
pyfprint/pyfprint.py | 427 ++++++++++++++++++++++++++++++++++++++++++++++
pyfprint/pyfprint_swig.i | 236 +++++++++++++++++++++++++
9 files changed, 1150 insertions(+), 3 deletions(-)
create mode 100644 m4/ac_pkg_swig.m4
create mode 100644 m4/ac_python_devel.m4
create mode 100644 m4/swig_python.m4
create mode 100644 pyfprint/Makefile.am
create mode 100644 pyfprint/pyfprint.py
create mode 100644 pyfprint/pyfprint_swig.i
diff --git a/Makefile.am b/Makefile.am
index 4c63a15..ffe45ef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,10 +4,16 @@ DISTCLEANFILES = ChangeLog libfprint.pc
SUBDIRS = libfprint doc
+ACLOCAL_AMFLAGS = -I m4
+
if BUILD_EXAMPLES
SUBDIRS += examples
endif
+if PYTHON
+SUBDIRS += pyfprint
+endif
+
pkgconfigdir=$(libdir)/pkgconfig
pkgconfig_DATA=libfprint.pc
diff --git a/autogen.sh b/autogen.sh
index 5499285..374db0f 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,8 +1,8 @@
#!/bin/sh
libtoolize --copy --force || exit 1
-aclocal || exit 1
+aclocal -I m4 || exit 1
autoheader || exit 1
-autoconf || exit 1
+autoconf -I m4 || exit 1
automake -a -c || exit 1
./configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*
diff --git a/configure.ac b/configure.ac
index 88f9641..6db68ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,6 +3,8 @@ AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([libfprint/core.c])
AM_CONFIG_HEADER([config.h])
+dnl AC_DISABLE_STATIC
+
AC_PREREQ([2.50])
AC_PROG_CC
AC_PROG_LIBTOOL
@@ -34,6 +36,18 @@ PKG_CHECK_MODULES(IMAGEMAGICK, "ImageMagick")
AC_SUBST(IMAGEMAGICK_CFLAGS)
AC_SUBST(IMAGEMAGICK_LIBS)
+AC_ARG_ENABLE([python], [AS_HELP_STRING([--enable-python],
+ [enable python wrapper (default n)])],
+ [pyfprint=$enableval],
+ [pyfprint='no'])
+AM_CONDITIONAL([PYTHON], [test "x$python" != "xno"])
+
+if test "x$python" != "xno" ; then
+ AM_PATH_PYTHON(2.3)
+ AC_PROG_SWIG(1.3.31)
+ SWIG_PYTHON
+fi
+
# Examples build
AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build],
[build example applications (default n)])],
@@ -99,6 +113,6 @@ AC_DEFINE([API_EXPORTED],
[__attribute__((visibility("default")))], [Default vis
AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused
-Wstrict-prototypes -Werror-implicit-function-declaration -Wno-pointer-sign
-Wshadow"
AC_SUBST(AM_CFLAGS)
-AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile]
[examples/Makefile] [doc/Makefile])
+AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile]
[examples/Makefile] [doc/Makefile] [pyfprint/Makefile])
AC_OUTPUT
diff --git a/m4/ac_pkg_swig.m4 b/m4/ac_pkg_swig.m4
new file mode 100644
index 0000000..738f69d
--- /dev/null
+++ b/m4/ac_pkg_swig.m4
@@ -0,0 +1,122 @@
+# ===========================================================================
+# http://autoconf-archive.cryp.to/ac_pkg_swig.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AC_PROG_SWIG([major.minor.micro])
+#
+# DESCRIPTION
+#
+# This macro searches for a SWIG installation on your system. If found you
+# should call SWIG via $(SWIG). You can use the optional first argument to
+# check if the version of the available SWIG is greater than or equal to
+# the value of the argument. It should have the format: N[.N[.N]] (N is a
+# number between 0 and 999. Only the first N is mandatory.)
+#
+# If the version argument is given (e.g. 1.3.17), AC_PROG_SWIG checks that
+# the swig package is this version number or higher.
+#
+# In configure.in, use as:
+#
+# AC_PROG_SWIG(1.3.17)
+# SWIG_ENABLE_CXX
+# SWIG_MULTI_MODULE_SUPPORT
+# SWIG_PYTHON
+#
+# LAST MODIFICATION
+#
+# 2008-04-12
+#
+# COPYLEFT
+#
+# Copyright (c) 2008 Sebastian Huber <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Alan W. Irwin <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Rafael Laboissiere <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Andrew Collier <[EMAIL PROTECTED]>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Macro Archive. When you make and
+# distribute a modified version of the Autoconf Macro, you may extend this
+# special exception to the GPL to apply to your modified version as well.
+
+AC_DEFUN([AC_PROG_SWIG],[
+ AC_PATH_PROG([SWIG],[swig])
+ if test -z "$SWIG" ; then
+ AC_MSG_WARN([cannot find 'swig' program. You should look at
http://www.swig.org])
+ SWIG='echo "Error: SWIG is not installed. You should look at
http://www.swig.org" ; false'
+ elif test -n "$1" ; then
+ AC_MSG_CHECKING([for SWIG version])
+ [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed
's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
+ AC_MSG_RESULT([$swig_version])
+ if test -n "$swig_version" ; then
+ # Calculate the required version number components
+ [required=$1]
+ [required_major=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_major" ; then
+ [required_major=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_minor=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_minor" ; then
+ [required_minor=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_patch=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_patch" ; then
+ [required_patch=0]
+ fi
+ # Calculate the available version number components
+ [available=$swig_version]
+ [available_major=`echo $available | sed
's/[^0-9].*//'`]
+ if test -z "$available_major" ; then
+ [available_major=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_minor=`echo $available | sed
's/[^0-9].*//'`]
+ if test -z "$available_minor" ; then
+ [available_minor=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_patch=`echo $available | sed
's/[^0-9].*//'`]
+ if test -z "$available_patch" ; then
+ [available_patch=0]
+ fi
+ if test $available_major -ne $required_major \
+ -o $available_minor -ne $required_minor \
+ -o $available_patch -lt $required_patch ; then
+ AC_MSG_WARN([SWIG version >= $1 is required.
You have $swig_version. You should look at http://www.swig.org])
+ SWIG='echo "Error: SWIG version >= $1 is
required. You have '"$swig_version"'. You should look at http://www.swig.org"
; false'
+ else
+ AC_MSG_NOTICE([SWIG executable is '$SWIG'])
+ SWIG_LIB=`$SWIG -swiglib`
+ AC_MSG_NOTICE([SWIG library directory is
'$SWIG_LIB'])
+ fi
+ else
+ AC_MSG_WARN([cannot determine SWIG version])
+ SWIG='echo "Error: Cannot determine SWIG version. You
should look at http://www.swig.org" ; false'
+ fi
+ fi
+ AC_SUBST([SWIG_LIB])
+])
diff --git a/m4/ac_python_devel.m4 b/m4/ac_python_devel.m4
new file mode 100644
index 0000000..7cec10f
--- /dev/null
+++ b/m4/ac_python_devel.m4
@@ -0,0 +1,265 @@
+# ===========================================================================
+# http://autoconf-archive.cryp.to/ac_python_devel.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AC_PYTHON_DEVEL([version])
+#
+# DESCRIPTION
+#
+# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
+# in your configure.ac.
+#
+# This macro checks for Python and tries to get the include path to
+# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS)
+# output variables. It also exports $(PYTHON_EXTRA_LIBS) and
+# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
+#
+# You can search for some particular version of Python by passing a
+# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
+# note that you *have* to pass also an operator along with the version to
+# match, and pay special attention to the single quotes surrounding the
+# version number. Don't use "PYTHON_VERSION" for this: that environment
+# variable is declared as precious and thus reserved for the end-user.
+#
+# This macro should work for all versions of Python >= 2.1.0. As an end
+# user, you can disable the check for the python version by setting the
+# PYTHON_NOVERSIONCHECK environment variable to something else than the
+# empty string.
+#
+# If you need to use this macro for an older Python version, please
+# contact the authors. We're always open for feedback.
+#
+# LAST MODIFICATION
+#
+# 2008-04-12
+#
+# COPYLEFT
+#
+# Copyright (c) 2008 Sebastian Huber <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Alan W. Irwin <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Rafael Laboissiere <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Andrew Collier <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Matteo Settenvini <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Horst Knorr <[EMAIL PROTECTED]>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Macro Archive. When you make and
+# distribute a modified version of the Autoconf Macro, you may extend this
+# special exception to the GPL to apply to your modified version as well.
+
+AC_DEFUN([AC_PYTHON_DEVEL],[
+ #
+ # Allow the use of a (user set) custom python version
+ #
+ AC_ARG_VAR([PYTHON_VERSION],[The installed Python
+ version to use, for example '2.3'. This string
+ will be appended to the Python interpreter
+ canonical name.])
+
+ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
+ if test -z "$PYTHON"; then
+ AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for a version of Python >= 2.1.0
+ #
+ AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+ ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+ ver = string.split(sys.version)[[0]]; \
+ print ver >= '2.1.0'"`
+ if test "$ac_supports_python_ver" != "True"; then
+ if test -z "$PYTHON_NOVERSIONCHECK"; then
+ AC_MSG_RESULT([no])
+ AC_MSG_FAILURE([
+This version of the AC@&[EMAIL PROTECTED] macro
+doesn't work properly with versions of Python before
+2.1.0. You may need to re-run configure, setting the
+variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
+PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
+Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
+to something else than an empty string.
+])
+ else
+ AC_MSG_RESULT([skip at user request])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ fi
+
+ #
+ # if the macro parameter ``version'' is set, honour it
+ #
+ if test -n "$1"; then
+ AC_MSG_CHECKING([for a version of Python $1])
+ ac_supports_python_ver=`$PYTHON -c "import sys, string; \
+ ver = string.split(sys.version)[[0]]; \
+ print ver $1"`
+ if test "$ac_supports_python_ver" = "True"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([this package requires Python $1.
+If you have it installed, but it isn't the default Python
+interpreter in your system path, please pass the PYTHON_VERSION
+variable to configure. See ``configure --help'' for reference.
+])
+ PYTHON_VERSION=""
+ fi
+ fi
+
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the distutils Python package])
+ ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+ if test -z "$ac_distutils_result"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot import Python module "distutils".
+Please check your Python installation. The error was:
+$ac_distutils_result])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for Python include path
+ #
+ AC_MSG_CHECKING([for Python include path])
+ if test -z "$PYTHON_CPPFLAGS"; then
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print distutils.sysconfig.get_python_inc();"`
+ if test -n "${python_path}"; then
+ python_path="-I$python_path"
+ fi
+ PYTHON_CPPFLAGS=$python_path
+ fi
+ AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+ AC_SUBST([PYTHON_CPPFLAGS])
+
+ #
+ # Check for Python library path
+ #
+ AC_MSG_CHECKING([for Python library path])
+ if test -z "$PYTHON_LDFLAGS"; then
+ # (makes two attempts to ensure we've got a version number
+ # from the interpreter)
+ py_version=`$PYTHON -c "from distutils.sysconfig import *; \
+ from string import join; \
+ print join(get_config_vars('VERSION'))"`
+ if test "$py_version" == "[None]"; then
+ if test -n "$PYTHON_VERSION"; then
+ py_version=$PYTHON_VERSION
+ else
+ py_version=`$PYTHON -c "import sys; \
+ print sys.version[[:3]]"`
+ fi
+ fi
+
+ PYTHON_LDFLAGS=`$PYTHON -c "from distutils.sysconfig import *; \
+ from string import join; \
+ print '-L' + get_python_lib(0,1), \
+ '-lpython';"`$py_version
+ fi
+ AC_MSG_RESULT([$PYTHON_LDFLAGS])
+ AC_SUBST([PYTHON_LDFLAGS])
+
+ #
+ # Check for site packages
+ #
+ AC_MSG_CHECKING([for Python site-packages path])
+ if test -z "$PYTHON_SITE_PKG"; then
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print distutils.sysconfig.get_python_lib(0,0);"`
+ fi
+ AC_MSG_RESULT([$PYTHON_SITE_PKG])
+ AC_SUBST([PYTHON_SITE_PKG])
+
+ #
+ # libraries which must be linked in when embedding
+ #
+ AC_MSG_CHECKING(python extra libraries)
+ if test -z "$PYTHON_EXTRA_LIBS"; then
+ PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print conf('LOCALMODLIBS'), conf('LIBS')"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+ AC_SUBST(PYTHON_EXTRA_LIBS)
+
+ #
+ # linking flags needed when embedding
+ #
+ AC_MSG_CHECKING(python extra linking flags)
+ if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print conf('LINKFORSHARED')"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+ AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+ #
+ # final check to see if everything compiles alright
+ #
+ AC_MSG_CHECKING([consistency of all components of python development
environment])
+ AC_LANG_PUSH([C])
+ # save current global flags
+ LIBS="$ac_save_LIBS $PYTHON_LDFLAGS"
+ CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+ AC_TRY_LINK([
+ #include <Python.h>
+ ],[
+ Py_Initialize();
+ ],[pythonexists=yes],[pythonexists=no])
+
+ AC_MSG_RESULT([$pythonexists])
+
+ if test ! "$pythonexists" = "yes"; then
+ AC_MSG_ERROR([
+ Could not link test program to Python. Maybe the main Python library has been
+ installed in some non-standard library path. If so, pass it to configure,
+ via the LDFLAGS environment variable.
+ Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
+ ============================================================================
+ ERROR!
+ You probably have to install the development version of the Python package
+ for your distribution. The exact name of this package varies among them.
+ ============================================================================
+ ])
+ PYTHON_VERSION=""
+ fi
+ AC_LANG_POP
+ # turn back to default flags
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBS="$ac_save_LIBS"
+
+ #
+ # all done!
+ #
+])
diff --git a/m4/swig_python.m4 b/m4/swig_python.m4
new file mode 100644
index 0000000..2496976
--- /dev/null
+++ b/m4/swig_python.m4
@@ -0,0 +1,65 @@
+# ===========================================================================
+# http://autoconf-archive.cryp.to/swig_python.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# SWIG_PYTHON([use-shadow-classes = {no, yes}])
+#
+# DESCRIPTION
+#
+# Checks for Python and provides the $(SWIG_PYTHON_CPPFLAGS), and
+# $(SWIG_PYTHON_OPT) output variables.
+#
+# $(SWIG_PYTHON_OPT) contains all necessary SWIG options to generate code
+# for Python. Shadow classes are enabled unless the value of the optional
+# first argument is exactly 'no'. If you need multi module support
+# (provided by the SWIG_MULTI_MODULE_SUPPORT macro) use
+# $(SWIG_PYTHON_LIBS) to link against the appropriate library. It contains
+# the SWIG Python runtime library that is needed by the type check system
+# for example.
+#
+# LAST MODIFICATION
+#
+# 2008-04-12
+#
+# COPYLEFT
+#
+# Copyright (c) 2008 Sebastian Huber <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Alan W. Irwin <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Rafael Laboissiere <[EMAIL PROTECTED]>
+# Copyright (c) 2008 Andrew Collier <[EMAIL PROTECTED]>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Macro Archive. When you make and
+# distribute a modified version of the Autoconf Macro, you may extend this
+# special exception to the GPL to apply to your modified version as well.
+
+AC_DEFUN([SWIG_PYTHON],[
+ AC_REQUIRE([AC_PROG_SWIG])
+ AC_REQUIRE([AC_PYTHON_DEVEL])
+ test "x$1" != "xno" || swig_shadow=" -noproxy"
+ AC_SUBST([SWIG_PYTHON_OPT],[-python$swig_shadow])
+ AC_SUBST([SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
+])
diff --git a/pyfprint/Makefile.am b/pyfprint/Makefile.am
new file mode 100644
index 0000000..51fdc48
--- /dev/null
+++ b/pyfprint/Makefile.am
@@ -0,0 +1,12 @@
+BUILT_SOURCES = pyfprint_swig.c
+SWIG_SOURCES = pyfprint_swig.i
+
+pkgpython_PYTHON = pyfprint_swig.py pyfprint.py
+pkgpyexec_LTLIBRARIES = _pyfprint_swig.la
+_pyfprint_swig_la_SOURCES = pyfprint_swig.c $(SWIG_SOURCES)
+_pyfprint_swig_la_CPPFLAGS = $(SWIG_PYTHON_CPPFLAGS) $(PYTHON_CPPFLAGS)
-I$(top_srcdir)/libfprint
+_pyfprint_swig_la_LDFLAGS = -module
+_pyfprint_swig_la_LIBADD = ../libfprint/libfprint.la
+
+pyfprint_swig.c : $(SWIG_SOURCES)
+ $(SWIG) $(SWIG_PYTHON_OPT) -I$(top_srcdir)/libfprint -o $@ $<
diff --git a/pyfprint/pyfprint.py b/pyfprint/pyfprint.py
new file mode 100644
index 0000000..1b77dc7
--- /dev/null
+++ b/pyfprint/pyfprint.py
@@ -0,0 +1,427 @@
+
+import pyfprint_swig as pyf
+
+# TODO:
+# exceptions, especially for RETRY_* errors
+# constants for fingers
+# tests
+# documentation
+# for x in y => map ?
+# Image(img) for devices which don't support imaging? Is img NULL?
+
+_init_ok = False
+
+def _dbg(*arg):
+ #print arg
+ pass
+
+def fp_init():
+ _init_ok = (pyf.fp_init() == 0)
+ if not _init_ok:
+ raise "fprint initialization failed."
+
+def fp_exit():
+ pyf.fp_exit()
+ _init_ok = False
+
+Fingers = dict(
+ LEFT_THUMB = pyf.LEFT_THUMB,
+ LEFT_INDEX = pyf.LEFT_INDEX,
+ LEFT_MIDDLE = pyf.LEFT_MIDDLE,
+ LEFT_RING = pyf.LEFT_RING,
+ LEFT_LITTLE = pyf.LEFT_LITTLE,
+ RIGHT_THUMB = pyf.RIGHT_THUMB,
+ RIGHT_INDEX = pyf.RIGHT_INDEX,
+ RIGHT_MIDDLE = pyf.RIGHT_MIDDLE,
+ RIGHT_RING = pyf.RIGHT_RING,
+ RIGHT_LITTLE = pyf.RIGHT_LITTLE
+ )
+
+class Device:
+ def __init__(self, dev_ptr = None, dscv_ptr = None, DscvList = None):
+ self.dev = dev_ptr
+ self.dscv = dscv_ptr
+ self.DscvList = DscvList
+ if dscv_ptr and DscvList == None:
+ raise "Programming error? Device contructed with dscv
without DscvList."
+
+ def close(self):
+ if self.dev:
+ pyf.fp_dev_close(self.dev)
+ self.dev = None
+
+ def open(self):
+ if self.dev:
+ raise "Device already open"
+ self.dev = pyf.fp_dev_open(self.dscv)
+ if not self.dev:
+ raise "device open failed"
+
+ def get_driver(self):
+ if self.dev:
+ return Driver(pyf.fp_dev_get_driver(self.dev))
+ if self.dscv:
+ return Driver(pyf.fp_dscv_dev_get_driver(self.dscv))
+
+ def get_devtype(self):
+ if self.dev:
+ return pyf.fp_dev_get_devtype(self.dev)
+ if self.dscv:
+ return pyf.fp_dscv_dev_get_devtype(self.dev)
+
+ def get_nr_enroll_stages(self):
+ if self.dev:
+ return pyf.fp_dev_get_nr_enroll_stages(self.dev)
+ raise "Device not open"
+
+ def is_compatible(self, fprint):
+ if self.dev:
+ if fprint.data_ptr:
+ return pyf.fp_dev_supports_print_data(self.dev,
fprint.data_ptr) == 1
+ if fprint.dscv_ptr:
+ return pyf.fp_dev_supports_dscv_print(self.dev,
fprint.dscv_ptr) == 1
+ raise "No print found"
+ if self.dscv:
+ if fprint.data_ptr:
+ return
pyf.fp_dscv_dev_supports_print_data(self.dscv, fprint.data_ptr) == 1
+ if fprint.dscv_ptr:
+ return
pyf.fp_dscv_dev_supports_dscv_print(self.dscv, fprint.dscv_ptr) == 1
+ raise "No print found"
+ raise "No device found"
+
+ def get_supports_imaging(self):
+ if self.dev:
+ return pyf.fp_dev_supports_imaging(self.dev) == 1
+ raise "Device not open"
+
+ def get_img_width(self):
+ if self.dev:
+ return pyf.fp_dev_get_img_width(self.dev)
+ raise "Device not open"
+
+ def get_img_height(self):
+ if self.dev:
+ return pyf.fp_dev_get_img_height(self.dev)
+ raise "Device not open"
+
+ def capture_image(self, wait_for_finger):
+ """FIXME: check that the dev supports imaging, or check
-ENOTSUP"""
+ if not self.dev:
+ raise "Device not open"
+
+ unconditional = 1
+ if wait_for_finger == True:
+ unconditional = 0
+
+ (r, img) = pyf.pyfp_dev_img_capture(self.dev, unconditional)
+ if r != 0:
+ raise "image_capture failed. error: " + r
+ return Image(img)
+
+ def enroll_finger(self):
+ if not self.dev:
+ raise "Device not open"
+ (r, fprint, img) = pyf.pyfp_enroll_finger_img(self.dev)
+ if r < 0:
+ raise "Internal I/O error while enrolling"
+ img = Image(img)
+ if r == pyf.FP_ENROLL_COMPLETE:
+ _dbg("enroll complete")
+ return (Fprint(data_ptr = fprint), img)
+ if r == pyf.FP_ENROLL_FAIL:
+ print "Failed. Enrollmet process reset."
+ if r == pyf.FP_ENROLL_PASS:
+ _dbg("enroll PASS")
+ return (None, img)
+ if r == pyf.FP_ENROLL_RETRY:
+ _dbg("enroll RETRY")
+ pass
+ if r == pyf.FP_ENROLL_RETRY_TOO_SHORT:
+ _dbg("enroll RETRY_SHORT")
+ pass
+ if r == pyf.FP_ENROLL_RETRY_CENTER_FINGER:
+ _dbg("enroll RETRY_CENTER")
+ pass
+ if r == pyf.FP_ENROLL_RETRY_REMOVE_FINGER:
+ _dbg("enroll RETRY_REMOVE")
+ pass
+ return ("xxx", None)
+
+ def verify_finger(self, fprint):
+ if not self.dev:
+ raise "Device not open"
+ (r, img) = pyf.pyfp_verify_finger_img(self.dev,
fprint._get_print_data_ptr())
+ if r < 0:
+ raise "verify error"
+ img = Image(img)
+ if r == pyf.FP_VERIFY_NO_MATCH:
+ return (False, img)
+ if r == pyf.FP_VERIFY_MATCH:
+ return (True, img)
+ if r == pyf.FP_VERIFY_RETRY:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_TOO_SHORT:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_CENTER_FINGER:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_REMOVE_FINGER:
+ pass
+ return (None, None)
+
+ def supports_identification(self):
+ if not self.dev:
+ raise "Device not open"
+ return pyf.fp_dev_supports_identification(self.dev) == 1
+
+ def identify_finger(self, fprints):
+ """Returns a tuple: (list_offset, Fprint, Image) if a match is
found,
+ (None, None, Image) otherwise. Image is None if the device
doesn't
+ support imaging."""
+
+ if not self.dev:
+ raise "Device not open"
+ gallery = pyf.pyfp_print_data_array(len(fprints))
+ for x in fprints:
+ if not self.is_compatible(x):
+ raise "can't verify uncompatible print"
+ gallery.append(x._get_print_data_ptr())
+ (r, offset, img) = pyf.pyfp_identify_finger_img(self.dev,
gallery.list)
+ if r < 0:
+ raise "identification error"
+ img = Image(img)
+ if r == pyf.FP_VERIFY_NO_MATCH:
+ return (None, None, img)
+ if r == pyf.FP_VERIFY_MATCH:
+ return (offset, fprints[offset], img)
+ if r == pyf.FP_VERIFY_RETRY:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_TOO_SHORT:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_CENTER_FINGER:
+ pass
+ if r == pyf.FP_VERIFY_RETRY_REMOVE_FINGER:
+ pass
+ return None
+
+ def load_print_from_disk(self, finger):
+ if not self.dev:
+ raise "Device not open"
+ (r, print_ptr) = pyf.fp_print_data_load(self.dev, finger)
+ if r != 0:
+ raise "could not load print from disk"
+ return Fprint(data_ptr = print_ptr)
+
+ def delete_stored_finger(self, finger):
+ if not self.dev:
+ raise "Device not open"
+ r = pyf.fp_print_data_delete(self.dev, finger)
+ if r != 0:
+ raise "delete failed"
+
+class Minutia(pyf.fp_minutia):
+ def __init__(self, minutia_ptr, img):
+ self.img = img
+ self.ptr = minutia_ptr
+ pyf.fp_minutia.__init__(self, minutia_ptr)
+
+class Image:
+ def __init__(self, img_ptr, bin = False):
+ self.img = img_ptr
+ self.bin = bin
+ self.std = False
+ self.minutiae = None
+
+ def __del__(self):
+ if self.img:
+ pyf.fp_img_free(self.img)
+
+ def get_height(self):
+ return pyf.fp_img_get_height(self.img)
+ def get_width(self):
+ return pyf.fp_img_get_width(self.img)
+
+ def get_data(self):
+ return pyf.pyfp_img_get_data(self.img)
+
+ def get_rgb_data(self):
+ return pyf.pyfp_img_get_rgb_data(self.img)
+
+ def save_to_file(self, path):
+ r = pyf.fp_img_save_to_file(self.img, path)
+ if r != 0:
+ raise "Save failed"
+
+ def standardize(self):
+ pyf.fp_img_standardize(self.img)
+ self.std = True
+
+ def binarize(self):
+ if self.bin:
+ return
+ if not self.std:
+ self.standardize()
+ i = pyf.fp_img_binarize(self.img)
+ if i == None:
+ raise "Binarize failed"
+ return Image(img_ptr = i, bin = True)
+
+ def get_minutiae(self):
+ if self.minutiae:
+ return self.minutiae
+ if self.bin:
+ raise "Cannot find minutiae in binarized image"
+ if not self.std:
+ self.standardize()
+ (min_list, nr) = pyf.fp_img_get_minutiae(self.img)
+ l = []
+ for n in range(nr):
+ l.append(Minutia(img = self, minutia_ptr =
pyf.pyfp_deref_minutiae(min_list, n)))
+ self.minutiae = l
+ return l
+
+class Driver:
+ def __init__(self, swig_drv_ptr):
+ self.drv = swig_drv_ptr
+
+ def __del__(self):
+ #FIXME: free drv?
+ pass
+
+ def get_name(self):
+ return pyf.fp_driver_get_name(self.drv)
+
+ def get_full_name(self):
+ return pyf.fp_driver_get_full_name(self.drv)
+
+ def get_driver_id(self):
+ return pyf.fp_driver_get_driver_id(self.drv)
+
+class Fprint:
+ def __init__(self, serial_data = None, data_ptr = None, dscv_ptr =
None, DscvList = None):
+ # data_ptr is a SWIG pointer to a struct pf_print_data
+ # dscv_ptr is a SWIG pointer to a struct pf_dscv_print
+ # DscvList is a class instance used to free the allocated
pf_dscv_print's
+ # with pf_dscv_prints_free when they're all unused.
+ # serial_data is a string as returned by get_data()
+
+ self.data_ptr = data_ptr
+ self.dscv_ptr = dscv_ptr
+ self.DscvList = DscvList
+
+ if serial_data:
+ self.data_ptr = pyf.fp_print_data_from_data(serial_data)
+ return
+
+ if dscv_ptr != None and DscvList == None:
+ raise "Programming error: Fprint constructed with
dscv_prt with DscvList == None"
+
+ def __del__(self):
+ if self.data_ptr:
+ pyf.fp_print_data_free(self.data_ptr)
+ # The dscv_ptr is freed when all the dscv prints have been
garbage collected
+
+ def _get_print_data_ptr(self):
+ if not self.data_ptr:
+ self._data_from_dscv()
+ return self.data_ptr
+
+ def get_driver_id(self):
+ if self.data_ptr:
+ return pyf.fp_print_data_get_driver_id(self.data_ptr)
+ elif self.dscv_ptr:
+ return pyf.fp_dscv_print_get_driver_id(self.dscv_ptr)
+ raise "no print"
+
+ def get_devtype(self):
+ if self.data_ptr:
+ return pyf.fp_print_data_get_devtype(self.data_ptr)
+ elif self.dscv_ptr:
+ return pyf.fp_dscv_print_get_devtype(self.dscv_ptr)
+ raise "no print"
+
+ def get_finger(self):
+ if not self.dscv_ptr:
+ raise "get_finger needs a discovered print"
+ return pyf.fp_dscv_print_get_finger(self.dscv_ptr)
+
+ def delete_from_disk(self):
+ if not self.dscv_ptr:
+ raise "delete needs a discovered print"
+ return pyf.fp_dscv_print_delete(self.dscv_ptr)
+
+ def save_to_disk(self, finger):
+ r = pyf.fp_print_data_save(self.data_ptr, finger)
+ if r != 0:
+ raise "save failed"
+
+ def _data_from_dscv(self):
+ if self.data_ptr:
+ return
+ if not self.dscv_ptr:
+ raise "no print"
+ (r, ptr) = pyf.fp_print_data_from_dscv_print(self.dscv_ptr)
+ if r != 0:
+ raise "print data from dscv failed"
+ self.data_ptr = ptr
+
+ def get_data(self):
+ if not self.data_ptr:
+ raise "no print"
+ s = pyf.pyfp_print_get_data(self.data_ptr)
+ if not len(s):
+ raise "serialization failed"
+ return s
+
+class DiscoveredPrints(list):
+ def __init__(self, dscv_devs_list):
+ self.ptr = dscv_devs_list
+ i = 0
+ while True:
+ x = pyf.pyfp_deref_dscv_print_ptr(dscv_devs_list, i)
+ if x == None:
+ break
+ self.append(Fprint(dscv_ptr = x, DscvList = self))
+ i = i + 1
+ def __del__(self):
+ pyf.pf_dscv_prints_free(self.ptr)
+
+def discover_prints():
+ if not _init_ok:
+ fp_init()
+
+ prints = pyf.fp_discover_prints()
+
+ if not prints:
+ print "Print discovery failed"
+ return DiscoveredPrints(prints)
+
+
+class DiscoveredDevices(list):
+ def __init__(self, dscv_devs_list):
+ self.swig_list_ptr = dscv_devs_list
+ i = 0
+ while True:
+ x = pyf.pyfp_deref_dscv_dev_ptr(dscv_devs_list, i)
+ if x == None:
+ break
+ self.append(Device(dscv_ptr = x, DscvList = self))
+ i = i + 1
+
+ def __del__(self):
+ pyf.fp_dscv_devs_free(self.swig_list_ptr)
+
+ def find_compatible(self, fprint):
+ for n in self:
+ if n.is_compatible(fprint):
+ return n
+ return None
+
+def discover_devices():
+ if not _init_ok:
+ fp_init()
+
+ devs = pyf.fp_discover_devs()
+
+ if not devs:
+ raise "Device discovery failed"
+ return DiscoveredDevices(devs)
\ No newline at end of file
diff --git a/pyfprint/pyfprint_swig.i b/pyfprint/pyfprint_swig.i
new file mode 100644
index 0000000..84fc3b1
--- /dev/null
+++ b/pyfprint/pyfprint_swig.i
@@ -0,0 +1,236 @@
+%module pyfprint_swig
+%{
+#include <fprint.h>
+#include <errno.h>
+%}
+
+%feature("autodoc", "1");
+
+%include <typemaps.i>
+%include <cdata.i>
+%include <carrays.i>
+%include <cstring.i>
+
+%nodefaultctor;
+
+/* fp_dev_img_capture, fp_enroll_finger_img, fp_verify_finger_img,
fp_identify_finger_img */
+%typemap(argout) struct fp_img ** {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*$1, $*1_descriptor, 1);
+ $result = SWIG_AppendOutput($result, o);
+ /* FIXME: is a PY_DECREF(o) needed here ?*/
+}
+%typemap(in, numinputs=0) struct fp_img **(struct fp_img *img) {
+ $1 = &img;
+}
+
+/* fp_enroll_finger_img */
+%typemap(argout) struct fp_print_data **print_data = struct fp_img **;
+%typemap(in, numinputs=0) struct fp_print_data **print_data(struct
fp_print_data *data) {
+ $1 = &data;
+}
+
+/* fp_print_data_load, fp_print_data_from_dscv_print */
+%apply struct fp_print_data **print_data { struct fp_print_data **data };
+
+/* fp_identify_finger */
+%apply unsigned long *OUTPUT { size_t *match_offset };
+
+/* fp_print_data_from_data */
+%apply (char *STRING, int LENGTH) { (unsigned char *buf, size_t buflen) };
+
+/* fp_img_get_minutiae */
+%apply int *OUTPUT { int *nr_minutiae };
+
+/* Tell SWIG that we're freeing the pointers */
+%delobject fp_dscv_devs_free;
+%delobject fp_img_free;
+%delobject fp_print_data_free;
+%delobject fp_dscv_prints_free;
+%delobject fp_dev_close;
+%delobject pyfp_free_print_data_array;
+
+/* Tell SWIG that we're allocating new objects */
+%newobject pyfp_alloc_print_data_array;
+%newobject fp_dev_open;
+
+/* Image.get_minutiae() */
+%inline %{
+struct fp_minutia * pyfp_deref_minutiae(struct fp_minutia **ptr, int i)
+{
+ return ptr[i];
+}
+
+%}
+/* The struct needs to be redefined as const, otherwise swig will generate
_set_ methods for the members. */
+struct fp_minutia {
+ const int x;
+ const int y;
+ const int ex;
+ const int ey;
+ const int direction;
+ const double reliability;
+ const int type;
+ const int appearing;
+ const int feature_id;
+ int * const nbrs;
+ int * const ridge_counts;
+ const int num_nbrs;
+
+ %extend {
+ /* A constructor that accepts pre-allocated structs */
+ fp_minutia(struct fp_minutia *ptr)
+ {
+ return ptr;
+ }
+ ~fp_minutia()
+ {
+ /* Don't free() fp_minutia *. They are free'd together
with the fp_img. */ ;
+ }
+ };
+};
+%ignore fp_minutia;
+
+/* Needed to get correct output from
+ fp_dscv_print_get_driver_id and fp_dev_get_devtype */
+typedef unsigned int uint32_t;
+/* fp_driver_get_driver_id, fp_dscv_print_get_driver_id,
fp_print_data_get_driver_id*/
+typedef unsigned short int uint16_t;
+
+/* Fprint.get_data() */
+%cstring_output_allocate_size(char **print_data, int *len, free(*($1)));
+%inline %{
+void pyfp_print_get_data(char **print_data, int *len, struct fp_print_data
*print)
+{
+ *len = fp_print_data_get_data(print, (unsigned char**)print_data);
+}
+%}
+%ignore fp_print_data_get_data;
+
+/* Img.get_data() */
+%cstring_output_allocate_size(char **img_data, int *len, "");
+%inline %{
+void pyfp_img_get_data(char **img_data, int *len, struct fp_img *img)
+{
+ *img_data = fp_img_get_data(img);
+ *len = fp_img_get_width(img) * fp_img_get_height(img);
+}
+%}
+%ignore fp_img_get_data;
+
+/* Image.get_rgb_data() */
+%cstring_output_allocate_size(char **img_rgb_data, int *len, free(*($1)));
+%inline %{
+void pyfp_img_get_rgb_data(char **img_rgb_data, int *len, struct fp_img *img)
+{
+ unsigned int i, j = 0;
+ unsigned char *img_data = fp_img_get_data(img);
+ *len = fp_img_get_width(img) * fp_img_get_height(img) * 3;
+ (*img_rgb_data) = malloc(*len);
+ for (i = 0; i < (*len)/3; i++) {
+ (*img_rgb_data)[j++] = img_data[i];
+ (*img_rgb_data)[j++] = img_data[i];
+ (*img_rgb_data)[j++] = img_data[i];
+ }
+}
+%}
+
+/* Wrappers to let Python yield the thread */
+%inline %{
+int pyfp_enroll_finger_img(struct fp_dev *dev, struct fp_print_data
**print_data, struct fp_img **img)
+{
+ int ret;
+ Py_BEGIN_ALLOW_THREADS
+ ret = fp_enroll_finger_img(dev, print_data, img);
+ Py_END_ALLOW_THREADS
+ return ret;
+}
+int pyfp_verify_finger_img(struct fp_dev *dev, struct fp_print_data
*enrolled_print, struct fp_img **img)
+{
+ int ret;
+ Py_BEGIN_ALLOW_THREADS
+ ret = fp_verify_finger_img(dev, enrolled_print, img);
+ Py_END_ALLOW_THREADS
+ return ret;
+}
+int pyfp_identify_finger_img(struct fp_dev *dev, struct fp_print_data
**print_gallery, size_t *match_offset, struct fp_img **img)
+{
+ int ret;
+ Py_BEGIN_ALLOW_THREADS
+ ret = fp_identify_finger_img(dev, print_gallery, match_offset, img);
+ Py_END_ALLOW_THREADS
+ return ret;
+}
+int pyfp_dev_img_capture(struct fp_dev *dev, int unconditional, struct fp_img
**image)
+{
+ int ret;
+ Py_BEGIN_ALLOW_THREADS
+ ret = fp_dev_img_capture(dev, unconditional, image);
+ Py_END_ALLOW_THREADS
+ return ret;
+}
+%}
+%ignore fp_enroll_finger_img;
+%ignore fp_enroll_finger;
+%ignore fp_verify_finger_img;
+%ignore fp_verify_finger;
+%ignore fp_identify_finger_img;
+%ignore fp_identify_finger;
+%ignore fp_dev_img_capture;
+
+
+%include "fprint.h"
+
+
+/* Device.identify_finger() */
+%inline %{
+struct pyfp_print_data_array {
+ size_t size;
+ size_t used;
+ struct fp_print_data * list[0];
+};
+%}
+%extend pyfp_print_data_array {
+ pyfp_print_data_array(size_t size)
+ {
+ struct pyfp_print_data_array *x;
+ x = calloc(1, sizeof(struct pyfp_print_data_array) +
+ sizeof(struct fp_print_data *) * (size + 1));
/* +1 for NULL termination */
+ x->size = size;
+ return x;
+ }
+ ~pyfp_print_data_array()
+ {
+ free($self);
+ }
+ void append(struct fp_print_data *print)
+ {
+ if ($self->size <= $self->used) {
+ PyErr_SetString(PyExc_OverflowError, "programming
error: pyfp_print_data_array list overflow");
+ return;
+ }
+ $self->list[$self->used] = print;
+ $self->used++;
+ }
+ struct fp_print_data ** pyfp_print_data_array_list_get()
+ {
+ return $self->list;
+ }
+};
+
+%inline %{
+
+/* DiscoveredDevices.__init__() */
+struct fp_dscv_dev * pyfp_deref_dscv_dev_ptr (struct fp_dscv_dev **ptr, int i)
+{
+ return ptr[i];
+}
+
+/* class DiscoveredPrints(list): */
+struct fp_dscv_print * pyfp_deref_dscv_print_ptr(struct fp_dscv_print **ptr,
int i)
+{
+ return ptr[i];
+}
+
+
+%}
--
1.5.4.5
_______________________________________________
fprint mailing list
[email protected]
http://lists.reactivated.net/mailman/listinfo/fprint