FaceOff is a simple web service that takes photographs and returns coordinates of detected faces, encapsulated with JSON. It uses a PHP-wrapped libface for the processing.
The source code is on GitHub.
***
These days I’m into wrappers.
As a personal exercise in wrapping C++ code with PHP, I decided to wrap libface, a C++ library for face detection that I worked on as my GSoC 2010 project for face tagging in digiKam. SWIG provides a rather simple way to do this. Here’s how to do it.
The interface file
SWIG gives you a ‘non-invasive’ way to wrap an API, in that you don’t have to modify the library at all. First, you’ll need to write an interface file – library_wrapped.i. Here, we’ve named it libface_php.i.
# Wrap everything under module libface_php %module libface_php %{ #include "/usr/include/libface/LibFaceConfig.h" #include "/usr/include/libface/Log.h" #include "/usr/include/libface/LibFaceCore.h" #include "/usr/include/libface/Face.h" #include "/usr/include/libface/LibFaceUtils.h" #include "/usr/include/libface/LibFace.h" #include "/usr/include/libface/Haarcascades.h" #include "/usr/include/libface/FaceDetect.h" #include "/usr/include/libface/Eigenfaces.h" #include "glue.cpp" %} %include std_string.i %include std_vector.i %include std_map.i %include cpointer.i %include /usr/include/libface/LibFaceConfig.h %include /usr/include/libface/Log.h %include /usr/include/libface/LibFaceCore.h %include /usr/include/libface/Face.h %include /usr/include/libface/LibFaceUtils.h %include /usr/include/libface/LibFace.h %include /usr/include/libface/Haarcascades.h %include /usr/include/libface/FaceDetect.h %include /usr/include/libface/Eigenfaces.h %include glue.cpp
The %module declares the name of the PHP extension. Once compiled, it will be named as ‘libface_php.so’.
This, of course, assumes that libface was installed under /usr – I couldn’t find a way to tell SWIG to prefix a path automatically upon invokation. The paths can be changed accordingly.
Glue
Next, SWIG isn’t perfect. And there are some things which make sense in one language but not in the other. For example, pointers in C++ are converted to resources in PHP. There may not be a dead-simple mapping from a vector type to an array type, for instance. To use some language-specific data structures, we need glue code.
glue.cpp:
#include using namespace std; using namespace libface; Face* faceAt(vector v, int index) { return (v[index]); }
So now we have the interface in place. Now comes the easy part – if all went well while writing the above; if it didn’t, you’ll have to try the next steps, test the wrapper, and then wrestle with the interface a bit (or a lot!) more and tweak it till it works.
Generating
First off, we have to generate a wrapper.
swig -c++ -php libface_php.i
This generates a file libface_php_wrap.cpp. This is the wrapper code that needs to be built and linked with libface.
Compile it:
g++ `php-config --includes` -fPIC -c libface_php_wrap.cpp
The -fPIC option instructs g++ to generate position-independent code.
Link it with libface and generate the extension:
g++ -shared libface_php_wrap.o -o libface_php.so -lface
The extension we wanted has now been created – libface_php.so.
Copy this to the PHP extensions directory:
cp libface_php.so `php-config --extension-dir`
In your system’s php.ini (/etc/php/php.ini in Arch), don’t forget to add the line:
extension=libface_php.so
You’re done. While doing this, a file libface_php.php was generated. This is the sole file that you will include in your php code.
Using it is rather simple:
require ("libface_php.php"); $instance = new Libface(DETECT); $filename = "test.png"; $detected_faces = $instance->detectFaces($filename); $count = $detected_faces->size(); // get the coordinates of the first face if($count >= 1) { $face = new Face(faceAt($detected_faces, 0)); // the face at index zero in the vector // Top left and bottom right coordinates echo "First face is : (" . $face->getX1() . "," . $face->getY1() . "),(" . $face->getX2() . "," . $face->getY2() . ")"; }
That’s it. Of course, this may not be the best way to wrap it, so suggestions are welcome.