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
|
config.sub
|
||||||
configure
|
configure
|
||||||
configure.ac
|
configure.ac
|
||||||
|
configure.in
|
||||||
include
|
include
|
||||||
install-sh
|
install-sh
|
||||||
libtool
|
libtool
|
||||||
@@ -35,4 +36,4 @@ tests/*/*.log
|
|||||||
tests/*/*.sh
|
tests/*/*.sh
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
|
|||||||
@@ -75,5 +75,5 @@ var_dump($landmarks);
|
|||||||
- [x] 1.Face Detection
|
- [x] 1.Face Detection
|
||||||
- [x] 2.Face Landmark Detection
|
- [x] 2.Face Landmark Detection
|
||||||
- [ ] 3.Deep Face Recognition
|
- [ ] 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 \
|
pdlib_src_files="pdlib.cc \
|
||||||
src/face_detection.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)
|
AC_MSG_CHECKING(for pkg-config)
|
||||||
if test ! -f "$PKG_CONFIG"; then
|
if test ! -f "$PKG_CONFIG"; then
|
||||||
@@ -49,4 +50,4 @@ if test "$PHP_PDLIB" != "no"; then
|
|||||||
PHP_EVAL_INCLINE($LIBDLIB_CFLAGS)
|
PHP_EVAL_INCLINE($LIBDLIB_CFLAGS)
|
||||||
|
|
||||||
PHP_NEW_EXTENSION(pdlib, $pdlib_src_files, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
|
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 "php_pdlib.h"
|
||||||
#include "src/face_detection.h"
|
#include "src/face_detection.h"
|
||||||
|
#include "src/cnn_face_detection.h"
|
||||||
#include "src/face_landmark_detection.h"
|
#include "src/face_landmark_detection.h"
|
||||||
|
|
||||||
/* If you declare any globals in php_pdlib.h uncomment this:
|
/* 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 */
|
/* True global resources - no need for thread safety here */
|
||||||
static int le_pdlib;
|
static int le_pdlib;
|
||||||
|
|
||||||
|
static zend_class_entry *cnn_face_detection_ce = nullptr;
|
||||||
|
static zend_object_handlers cnn_face_detection_obj_handlers;
|
||||||
|
|
||||||
/* {{{ PHP_INI
|
/* {{{ PHP_INI
|
||||||
*/
|
*/
|
||||||
/* Remove comments and fill if you need to have entries in 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
|
||||||
*/
|
*/
|
||||||
PHP_MINIT_FUNCTION(pdlib)
|
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
|
/* If you have INI entries, uncomment these lines
|
||||||
REGISTER_INI_ENTRIES();
|
REGISTER_INI_ENTRIES();
|
||||||
*/
|
*/
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ PHP_MSHUTDOWN_FUNCTION
|
/* {{{ 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