Merge pull request #3 from stalker314314/cnn_face_detection
Adding support for cnn face detector
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -15,6 +15,7 @@ config.status
|
||||
config.sub
|
||||
configure
|
||||
configure.ac
|
||||
configure.in
|
||||
include
|
||||
install-sh
|
||||
libtool
|
||||
@@ -35,4 +36,4 @@ tests/*/*.log
|
||||
tests/*/*.sh
|
||||
|
||||
.idea
|
||||
cmake-build-debug
|
||||
cmake-build-debug
|
||||
|
||||
@@ -75,5 +75,5 @@ var_dump($landmarks);
|
||||
- [x] 1.Face Detection
|
||||
- [x] 2.Face Landmark Detection
|
||||
- [ ] 3.Deep Face Recognition
|
||||
- [ ] 4.Deep Learning Face Detection
|
||||
- [x] 4.Deep Learning Face Detection
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ if test "$PHP_PDLIB" != "no"; then
|
||||
|
||||
pdlib_src_files="pdlib.cc \
|
||||
src/face_detection.cc \
|
||||
src/face_landmark_detection.cc"
|
||||
src/face_landmark_detection.cc \
|
||||
src/cnn_face_detection.cc"
|
||||
|
||||
AC_MSG_CHECKING(for pkg-config)
|
||||
if test ! -f "$PKG_CONFIG"; then
|
||||
@@ -49,4 +50,4 @@ if test "$PHP_PDLIB" != "no"; then
|
||||
PHP_EVAL_INCLINE($LIBDLIB_CFLAGS)
|
||||
|
||||
PHP_NEW_EXTENSION(pdlib, $pdlib_src_files, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
||||
fi
|
||||
fi
|
||||
|
||||
36
pdlib.cc
36
pdlib.cc
@@ -29,6 +29,7 @@ extern "C" {
|
||||
}
|
||||
#include "php_pdlib.h"
|
||||
#include "src/face_detection.h"
|
||||
#include "src/cnn_face_detection.h"
|
||||
#include "src/face_landmark_detection.h"
|
||||
|
||||
/* If you declare any globals in php_pdlib.h uncomment this:
|
||||
@@ -38,6 +39,9 @@ ZEND_DECLARE_MODULE_GLOBALS(pdlib)
|
||||
/* True global resources - no need for thread safety here */
|
||||
static int le_pdlib;
|
||||
|
||||
static zend_class_entry *cnn_face_detection_ce = nullptr;
|
||||
static zend_object_handlers cnn_face_detection_obj_handlers;
|
||||
|
||||
/* {{{ PHP_INI
|
||||
*/
|
||||
/* Remove comments and fill if you need to have entries in php.ini
|
||||
@@ -88,15 +92,47 @@ static void php_pdlib_init_globals(zend_pdlib_globals *pdlib_globals)
|
||||
*/
|
||||
/* }}} */
|
||||
|
||||
const zend_function_entry cnn_face_detection_class_methods[] = {
|
||||
PHP_ME(CnnFaceDetection, __construct, cnn_face_detection_ctor_arginfo, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(CnnFaceDetection, detect, cnn_face_detection_detect_arginfo, ZEND_ACC_PUBLIC)
|
||||
PHP_FE_END
|
||||
};
|
||||
|
||||
zend_object* php_cnn_face_detection_new(zend_class_entry *class_type TSRMLS_DC)
|
||||
{
|
||||
cnn_face_detection *cfd = (cnn_face_detection*)ecalloc(1, sizeof(cnn_face_detection));
|
||||
zend_object_std_init(&cfd->std, class_type TSRMLS_CC);
|
||||
object_properties_init(&cfd->std, class_type);
|
||||
cfd->std.handlers = &cnn_face_detection_obj_handlers; //zend_get_std_object_handlers();
|
||||
|
||||
return &cfd->std;
|
||||
}
|
||||
|
||||
static void php_cnn_face_detection_free(zend_object *object)
|
||||
{
|
||||
cnn_face_detection *cfd = (cnn_face_detection*)((char*)object - XtOffsetOf(cnn_face_detection, std));
|
||||
delete cfd->net;
|
||||
zend_object_std_dtor(object);
|
||||
}
|
||||
|
||||
/* {{{ PHP_MINIT_FUNCTION
|
||||
*/
|
||||
PHP_MINIT_FUNCTION(pdlib)
|
||||
{
|
||||
zend_class_entry ce;
|
||||
INIT_CLASS_ENTRY(ce, "CnnFaceDetection", cnn_face_detection_class_methods);
|
||||
cnn_face_detection_ce = zend_register_internal_class(&ce TSRMLS_CC);
|
||||
cnn_face_detection_ce->create_object = php_cnn_face_detection_new;
|
||||
memcpy(&cnn_face_detection_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
|
||||
cnn_face_detection_obj_handlers.offset = XtOffsetOf(cnn_face_detection, std);
|
||||
cnn_face_detection_obj_handlers.free_obj = php_cnn_face_detection_free;
|
||||
|
||||
/* If you have INI entries, uncomment these lines
|
||||
REGISTER_INI_ENTRIES();
|
||||
*/
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ PHP_MSHUTDOWN_FUNCTION
|
||||
|
||||
108
src/cnn_face_detection.cc
Normal file
108
src/cnn_face_detection.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "../php_pdlib.h"
|
||||
#include "cnn_face_detection.h"
|
||||
|
||||
#include <zend_exceptions.h>
|
||||
#include <dlib/image_processing/frontal_face_detector.h>
|
||||
#include <dlib/gui_widgets.h>
|
||||
#include <dlib/image_io.h>
|
||||
#include <dlib/dnn.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace dlib;
|
||||
using namespace std;
|
||||
|
||||
static inline cnn_face_detection *php_cnn_face_detection_from_obj(zend_object *obj) {
|
||||
return (cnn_face_detection*)((char*)(obj) - XtOffsetOf(cnn_face_detection, std));
|
||||
}
|
||||
|
||||
#define Z_CNN_FACE_DETECTION_P(zv) php_cnn_face_detection_from_obj(Z_OBJ_P((zv)))
|
||||
|
||||
PHP_METHOD(CnnFaceDetection, __construct)
|
||||
{
|
||||
char *sz_cnn_face_detection_model_path;
|
||||
size_t cnn_face_detection_model_path_len;
|
||||
|
||||
cnn_face_detection *cfd = Z_CNN_FACE_DETECTION_P(getThis());
|
||||
|
||||
if (NULL == cfd) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unable to find obj in CnnFaceDetection::__construct()");
|
||||
return;
|
||||
}
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
|
||||
&sz_cnn_face_detection_model_path, &cnn_face_detection_model_path_len) == FAILURE){
|
||||
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Unable to parse face_detection_model_path");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
string cnn_face_detection_model_path(
|
||||
sz_cnn_face_detection_model_path, cnn_face_detection_model_path_len);
|
||||
net_type *pnet = new net_type;
|
||||
deserialize(cnn_face_detection_model_path) >> *pnet;
|
||||
cfd->net = pnet;
|
||||
} catch (exception& e) {
|
||||
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PHP_METHOD(CnnFaceDetection, detect)
|
||||
{
|
||||
char *img_path;
|
||||
size_t img_path_len;
|
||||
long upsample_num = 1;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &img_path, &img_path_len, &upsample_num) == FAILURE){
|
||||
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Unable to parse detect arguments");
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
try {
|
||||
cnn_face_detection *cfd = Z_CNN_FACE_DETECTION_P(getThis());
|
||||
|
||||
pyramid_down<2> pyr;
|
||||
matrix<rgb_pixel> img;
|
||||
load_image(img, img_path);
|
||||
|
||||
// Upsampling the image will allow us to detect smaller faces but will cause the
|
||||
// program to use more RAM and run longer.
|
||||
//
|
||||
unsigned int levels = upsample_num;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
pyramid_up(img, pyr);
|
||||
}
|
||||
|
||||
net_type *pnet = cfd->net;
|
||||
auto dets = (*pnet)(img);
|
||||
int rect_count = 0;
|
||||
array_init(return_value);
|
||||
|
||||
// Scale the detection locations back to the original image size
|
||||
// if the image was upscaled.
|
||||
//
|
||||
for (auto&& d: dets) {
|
||||
d.rect = pyr.rect_down(d.rect, upsample_num);
|
||||
// Create new assoc array with dimensions of found rectt and confidence
|
||||
//
|
||||
zval rect_arr;
|
||||
array_init(&rect_arr);
|
||||
add_assoc_long(&rect_arr, "left", d.rect.left());
|
||||
add_assoc_long(&rect_arr, "top", d.rect.top());
|
||||
add_assoc_long(&rect_arr, "right", d.rect.right());
|
||||
add_assoc_long(&rect_arr, "bottom", d.rect.bottom());
|
||||
add_assoc_double(&rect_arr, "detection_confidence", d.detection_confidence);
|
||||
// Add this assoc array to returned array
|
||||
//
|
||||
add_next_index_zval(return_value, &rect_arr);
|
||||
}
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
36
src/cnn_face_detection.h
Normal file
36
src/cnn_face_detection.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Created by branko at kokanovic dot org on 2018/7/16.
|
||||
//
|
||||
|
||||
#ifndef PHP_DLIB_CNN_FACE_DETECTION_H
|
||||
#define PHP_DLIB_CNN_FACE_DETECTION_H
|
||||
|
||||
#include <dlib/dnn.h>
|
||||
|
||||
using namespace dlib;
|
||||
|
||||
template <long num_filters, typename SUBNET> using con5d = con<num_filters,5,5,2,2,SUBNET>;
|
||||
template <long num_filters, typename SUBNET> using con5 = con<num_filters,5,5,1,1,SUBNET>;
|
||||
|
||||
template <typename SUBNET> using downsampler = relu<affine<con5d<32, relu<affine<con5d<32, relu<affine<con5d<16,SUBNET>>>>>>>>>;
|
||||
template <typename SUBNET> using rcon5 = relu<affine<con5<45,SUBNET>>>;
|
||||
|
||||
using net_type = loss_mmod<con<1,9,9,1,1,rcon5<rcon5<rcon5<downsampler<input_rgb_image_pyramid<pyramid_down<6>>>>>>>>;
|
||||
|
||||
typedef struct _cnn_face_detection {
|
||||
net_type *net;
|
||||
zend_object std;
|
||||
} cnn_face_detection;
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(cnn_face_detection_ctor_arginfo, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, cnn_face_detection_model_path)
|
||||
ZEND_END_ARG_INFO()
|
||||
PHP_METHOD(CnnFaceDetection, __construct);
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(cnn_face_detection_detect_arginfo, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, img_path)
|
||||
ZEND_ARG_INFO(0, upsample_num)
|
||||
ZEND_END_ARG_INFO()
|
||||
PHP_METHOD(CnnFaceDetection, detect);
|
||||
|
||||
#endif //PHP_DLIB_CNN_FACE_DETECTION_H
|
||||
15
tests/cnn_face_detection_ctor_error.phpt
Normal file
15
tests/cnn_face_detection_ctor_error.phpt
Normal file
@@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Testing CnnFaceDetection constructor without arguments
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
try {
|
||||
new CnnFaceDetection();
|
||||
} catch (Exception $e) {
|
||||
var_dump($e->getMessage());
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
Warning: CnnFaceDetection::__construct() expects exactly 1 parameter, 0 given in /home/branko/pdlib/tests/cnn_face_detection_ctor_error.php on line 3
|
||||
string(41) "Unable to parse face_detection_model_path"
|
||||
14
tests/cnn_face_detection_ctor_model_not_found_error.phpt
Normal file
14
tests/cnn_face_detection_ctor_model_not_found_error.phpt
Normal file
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
Testing CnnFaceDetection constructor with model that do not exist
|
||||
--SKIPIF--
|
||||
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||
--FILE--
|
||||
<?php
|
||||
try {
|
||||
new CnnFaceDetection("foo");
|
||||
} catch (Exception $e) {
|
||||
var_dump($e->getMessage());
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
string(31) "Unable to open foo for reading."
|
||||
Reference in New Issue
Block a user