Introducing FaceOff and How To Write a PHP Extension For Your C++ API using SWIG

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.