-
Manuel Günther authoredManuel Günther authored
ext.cpp 6.38 KiB
/**
* @author Andre Anjos <andre.anjos@idiap.ch>
* @date Thu 20 Sep 2012 14:46:35 CEST
*
* @brief Boost.Python extension to flandmark
*/
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <bob/config.h>
#ifdef BOB_API_VERSION
# include <bob/python/gil.h>
# include <bob/python/ndarray.h>
#else
# include <bob/core/python/gil.h>
# include <bob/core/python/ndarray.h>
#endif
#include <cv.h>
#include "flandmark_detector.h"
using namespace boost::python;
static void delete_cascade(CvHaarClassifierCascade* o) {
cvReleaseHaarClassifierCascade(&o);
}
static void delete_flandmark(FLANDMARK_Model* o) {
flandmark_free(o);
}
static void delete_image(IplImage* i) {
i->imageData = 0; ///< never delete blitz::Array data
cvReleaseImage(&i);
}
static void delete_storage(CvMemStorage* s) {
cvClearMemStorage(s);
cvReleaseMemStorage(&s);
}
/**
* A simple wrapper to operate the flandmark library quickly in iterative
* environments like Python.
*/
class Localizer {
public: //api
/**
* Constructor, has to load a boosted cascaded from OpenCV and the
* flandmark model.
*/
Localizer(const std::string& opencv_cascade,
const std::string& flandmark_model) :
m_cascade((CvHaarClassifierCascade*)cvLoad(opencv_cascade.c_str(), 0, 0, 0), std::ptr_fun(delete_cascade)),
m_flandmark(flandmark_init(flandmark_model.c_str()), std::ptr_fun(delete_flandmark)),
m_storage(cvCreateMemStorage(0), std::ptr_fun(delete_storage))
{
if( !m_cascade ) {
PYTHON_ERROR(RuntimeError, "Couldnt load Face detector '%s'", opencv_cascade.c_str());
}
if ( !m_flandmark ) {
PYTHON_ERROR(RuntimeError, "Structure model wasn't created. Corrupted file '%s'", flandmark_model.c_str());
}
m_landmarks.reset(new double[2*m_flandmark->data.options.M]);
}
/**
* Detect and locates the landmarks from an input image
*/
tuple call1(bob::python::const_ndarray input) {
//checks type
const bob::core::array::typeinfo& type = input.type();
if ((type.dtype != bob::core::array::t_uint8) || (type.nd != 2)) {
PYTHON_ERROR(TypeError, "Input data must be a 2D numpy.array with dtype=uint8 (i.e. a gray-scaled image), but you passed %s", type.str().c_str());
}
//converts to IplImage
boost::shared_ptr<IplImage> ipl_image(cvCreateImageHeader(cvSize(type.shape[1], type.shape[0]), IPL_DEPTH_8U, 1), std::ptr_fun(delete_image));
ipl_image->imageData = (char*)input.bz<uint8_t,2>().data();
// Flags for OpenCV face detection
CvSize minFeatureSize = cvSize(40, 40);
int flags = CV_HAAR_DO_CANNY_PRUNING;
float search_scale_factor = 1.1f;
// Detect all the faces in the greyscale image.
CvSeq* rects = cvHaarDetectObjects(ipl_image.get(), m_cascade.get(),
m_storage.get(), search_scale_factor, 2, flags, minFeatureSize);
int nFaces = rects->total;
list retval;
for (int iface = 0; iface < (rects ? nFaces : 0); ++iface) {
CvRect* r = (CvRect*)cvGetSeqElem(rects, iface);
dict det;
det["bbox"] = make_tuple(r->x, r->y, r->width, r->height);
int bbox[4] = {r->x, r->y, r->x + r->width, r->y + r->height};
int flandmark_result;
{
bob::python::no_gil unlock;
flandmark_result = flandmark_detect(ipl_image.get(), bbox, m_flandmark.get(),
m_landmarks.get());
}
list lmlist; ///< landmark list
// do not copy the results when the landmark detector indicates an error.
// otherwise stale results (from a previous invocation) are returned
if (flandmark_result == NO_ERR) {
// The first point represents the center of the bounding box used by
// the flandmark library.
for (int i = 0; i < (2*m_flandmark->data.options.M); i += 2) {
lmlist.append(make_tuple(m_landmarks[i+1], m_landmarks[i]));
}
}
det["landmark"] = tuple(lmlist);
retval.append(det);
}
return tuple(retval);
}
/**
* Detect and locates the landmarks from an input image
*/
object call2(bob::python::const_ndarray input, const int b_y, const int b_x, const int b_height, const int b_width)
{
//checks type
const bob::core::array::typeinfo& type = input.type();
if ((type.dtype != bob::core::array::t_uint8) || (type.nd != 2)) {
PYTHON_ERROR(TypeError, "Input data must be a 2D numpy.array with dtype=uint8 (i.e. a gray-scaled image), but you passed %s", type.str().c_str());
}
//converts to IplImage
boost::shared_ptr<IplImage> ipl_image(cvCreateImageHeader(cvSize(type.shape[1], type.shape[0]), IPL_DEPTH_8U, 1), std::ptr_fun(delete_image));
ipl_image->imageData = (char*)input.bz<uint8_t,2>().data();
int bbox[4] = {b_x, b_y, b_x + b_width, b_y + b_height};
int flandmark_result;
{
bob::python::no_gil unlock;
flandmark_result = flandmark_detect(ipl_image.get(), bbox, m_flandmark.get(),
m_landmarks.get());
}
list lmlist; ///< landmark list
if (flandmark_result == NO_ERR) {
for (int i = 0; i < (2*m_flandmark->data.options.M); i += 2) {
lmlist.append(make_tuple(m_landmarks[i+1], m_landmarks[i]));
}
}
return object(lmlist);
}
private: //representation
boost::shared_ptr<CvHaarClassifierCascade> m_cascade;
boost::shared_ptr<FLANDMARK_Model> m_flandmark;
boost::shared_ptr<CvMemStorage> m_storage;
boost::shared_array<double> m_landmarks;
};
BOOST_PYTHON_MODULE(_flandmark) {
bob::python::setup_python("bindings to flandmark - a library for the localization of facial landmarks");
class_<Localizer>("Localizer", "A key-point localization for faces using flandmark", init<const std::string&, const std::string&>((arg("detector"), arg("localizer")), "Initializes with both an OpenCV face detector model and an flandmark model"))
.def("__call__", &Localizer::call1, (arg("image")), "Locates (possibly multiple) key-points on the given input image. Returns a list of located faces (by OpenCV's model), each attached to a list of key-points.")
.def("__call__", &Localizer::call2, (arg("image"), arg("b_y"), arg("b_x"), arg("b_height"), arg("b_width")), "Locates (possibly multiple) key-points on the given input image, given a bounding box and returns them as a list.")
;
}