Merge pull request #6 from stalker314314/face_recognition

Face recognition
This commit is contained in:
goodspb 2018-08-30 23:07:34 +08:00 committed by GitHub
commit ac65116a0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 468 additions and 32 deletions

View File

@ -1,16 +1,15 @@
# PDlib - A PHP extension for Dlib
A PHP extension
## Requirements
- Dlib 19.13+
- PHP 7.0+
- C++ 11
- C++11
## Dependence
## Dependencies
### Dlib
Install Dlib as share library
Install Dlib as shared library
```bash
git clone git@github.com:davisking/dlib.git
@ -33,23 +32,62 @@ make
sudo make install
```
## Configure
### Configure PHP installation
```
```bash
vim youpath/php.ini
```
Write the below content into `php.ini`
Append the content below into `php.ini`
```
[pdlib]
extension="pdlib.so"
```
## Tests
For tests, you will need to have bz2 extension installed. On Ubuntu, it boils to:
```bash
sudo apt-get install php-bz2
```
After you successfully compiled everything, just run:
```bash
make test
```
## Usage
### General Usage
Good starting point can be `tests/integration_face_recognition.phpt`. Check that first.
Basically, if you just quickly want to get from your image to 128D descriptor of faces in image,
here is really minimal example how:
```php
<?php
$img_path = "image.jpg";
$fd = new CnnFaceDetection("detection_cnn_model.dat");
$detected_faces = $fd->detect($img_path);
foreach($detected_faces as $detected_face) {
$fld = new FaceLandmarkDetection("landmark_model.dat");
$landmarks = $fld->detect($img_path, $detected_face);
$fr = new FaceRecognition("recognition_model.dat");
$descriptor = $fr->computeDescriptor($img_path, $landmarks);
// Optionally use descriptor later in `dlib_chinese_whispers` function
}
```
Location from where to get these models can be found on DLib website, as well as in `tests/integration_face_recognition.phpt` test.
### Specific use cases
#### face detection
If you want to use HOG based approach:
```php
<?php
@ -57,9 +95,19 @@ extension="pdlib.so"
$faceCount = dlib_face_detection("~/a.jpg");
// how mary face in the picture.
var_dump($faceCount);
```
If you want to use CNN approach (and CNN model):
```php
<?php
$fd = new CnnFaceDetection("detection_cnn_model.dat");
$detected_faces = $fd->detect("image.jpg");
// $detected_face is indexed array, where values are assoc arrays with "top", "bottom", "left" and "right" values
```
CNN model can get you slightly better results, but is much, much more demanding (CPU and memory, GPU is also preferred).
#### face landmark detection
```php
@ -68,7 +116,6 @@ var_dump($faceCount);
// face landmark detection
$landmarks = dlib_face_landmark_detection("~/a.jpg");
var_dump($landmarks);
```
Additionally, you can also use class-based approach:
@ -83,6 +130,19 @@ $parts = $fld->detect("path/to/image.jpg", $rect);
Note that, if you use class-based approach, you need to feed bounding box rectangle with values obtained from `dlib_face_detection`. If you use `dlib_face_landmark_detection`, everything is already done for you (and you are using HOG face detection model).
#### face recognition (aka getting face descriptor)
```php
<?php
$fr = new FaceRecognition($model_path);
$landmarks = array(
"rect" => $rect_of_faces_obtained_with_CnnFaceDetection,
"parts" => $parts_obtained_with_FaceLandmarkDetection);
$descriptor = $fr->computeDescriptor($img_path, $landmarks);
// $descriptor is 128D array
```
#### chinese whispers
Provides raw access to dlib's `chinese_whispers` function.
@ -98,13 +158,12 @@ Returned value is also numeric array, containing obtained labels.
// $labels will look like [0,0,1].
$edges = [[0,0], [0,1], [1,1], [2,2]];
$labels = dlib_chinese_whispers($edges);
```
## Features
- [x] 1.Face Detection
- [x] 2.Face Landmark Detection
- [ ] 3.Deep Face Recognition
- [x] 3.Deep Face Recognition
- [x] 4.Deep Learning Face Detection
- [x] 5. Raw chinese_whispers

View File

@ -28,7 +28,8 @@ if test "$PHP_PDLIB" != "no"; then
src/chinese_whispers.cc \
src/face_detection.cc \
src/face_landmark_detection.cc \
src/cnn_face_detection.cc"
src/face_recognition.cc \
src/cnn_face_detection.cc "
AC_MSG_CHECKING(for pkg-config)
if test ! -f "$PKG_CONFIG"; then

View File

@ -30,6 +30,7 @@ extern "C" {
#include "php_pdlib.h"
#include "src/chinese_whispers.h"
#include "src/face_detection.h"
#include "src/face_recognition.h"
#include "src/cnn_face_detection.h"
#include "src/face_landmark_detection.h"
@ -46,6 +47,9 @@ static zend_object_handlers cnn_face_detection_obj_handlers;
static zend_class_entry *face_landmark_detection_ce = nullptr;
static zend_object_handlers face_landmark_detection_obj_handlers;
static zend_class_entry *face_recognition_ce = nullptr;
static zend_object_handlers face_recognition_obj_handlers;
/* {{{ PHP_INI
*/
/* Remove comments and fill if you need to have entries in php.ini
@ -142,6 +146,29 @@ static void php_face_landmark_detection_free(zend_object *object)
zend_object_std_dtor(object);
}
const zend_function_entry face_recognition_class_methods[] = {
PHP_ME(FaceRecognition, __construct, face_recognition_ctor_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(FaceRecognition, computeDescriptor, face_recognition_compute_descriptor_arginfo, ZEND_ACC_PUBLIC)
PHP_FE_END
};
zend_object* php_face_recognition_new(zend_class_entry *class_type TSRMLS_DC)
{
face_recognition *fr = (face_recognition*)ecalloc(1, sizeof(face_recognition));
zend_object_std_init(&fr->std, class_type TSRMLS_CC);
object_properties_init(&fr->std, class_type);
fr->std.handlers = &face_recognition_obj_handlers;
return &fr->std;
}
static void php_face_recognition_free(zend_object *object)
{
face_recognition *fr = (face_recognition*)((char*)object - XtOffsetOf(face_recognition, std));
delete fr->net;
zend_object_std_dtor(object);
}
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(pdlib)
@ -165,6 +192,15 @@ PHP_MINIT_FUNCTION(pdlib)
face_landmark_detection_obj_handlers.offset = XtOffsetOf(face_landmark_detection, std);
face_landmark_detection_obj_handlers.free_obj = php_face_landmark_detection_free;
// FaceRecognition class definition
//
INIT_CLASS_ENTRY(ce, "FaceRecognition", face_recognition_class_methods);
face_recognition_ce = zend_register_internal_class(&ce TSRMLS_CC);
face_recognition_ce->create_object = php_face_recognition_new;
memcpy(&face_recognition_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
face_recognition_obj_handlers.offset = XtOffsetOf(face_recognition, std);
face_recognition_obj_handlers.free_obj = php_face_recognition_free;
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/

View File

@ -61,6 +61,22 @@ ZEND_END_MODULE_GLOBALS(pdlib)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
#define PARSE_LONG_FROM_ARRAY(hashtable, key, error_key_missing, error_key_not_long) \
zval* data##key; \
/* Tries to find given key in array */ \
data##key = zend_hash_str_find(hashtable, #key, sizeof(#key)-1); \
if (data##key == nullptr) { \
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, #error_key_missing); \
return; \
} \
\
/* We also need to check proper type of value in associative array */ \
if (Z_TYPE_P(data##key) != IS_LONG) { \
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, #error_key_not_long); \
return; \
} \
zend_long key = Z_LVAL_P(data##key); \
#endif /* PHP_PDLIB_H */

View File

@ -79,13 +79,13 @@ PHP_METHOD(CnnFaceDetection, detect)
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
// Create new assoc array with dimensions of found rect and confidence
//
zval rect_arr;
array_init(&rect_arr);

View File

@ -102,20 +102,8 @@ PHP_METHOD(FaceLandmarkDetection, __construct)
// Helper macro to automatically have parsing of "top"/"bottom"/"left"/"right"
#define PARSE_BOUNDING_BOX_EDGE(side) \
zval* data##side; \
/* Tries to find given key in array */ \
data##side = zend_hash_str_find(bounding_box_hash, #side, sizeof(#side)-1); \
if (data##side == nullptr) { \
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Bounding box (second argument) is missing " #side "key"); \
return; \
} \
\
/* We also need to check proper type of value in associative array */ \
if (Z_TYPE_P(data##side) != IS_LONG) { \
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Value of bounding box's (second argument) " #side " key is not long type"); \
return; \
} \
zend_long side = Z_LVAL_P(data##side); \
PARSE_LONG_FROM_ARRAY(bounding_box_hash, side, \
"Bounding box (second argument) is missing " #side "key", "Value of bounding box's (second argument) " #side " key is not long type")
PHP_METHOD(FaceLandmarkDetection, detect)
{
@ -134,8 +122,8 @@ PHP_METHOD(FaceLandmarkDetection, detect)
// Check that bounding box have exactly 4 elements
HashTable *bounding_box_hash = Z_ARRVAL_P(bounding_box);
uint32_t bounding_box_num_elements = zend_hash_num_elements(bounding_box_hash);
if (bounding_box_num_elements != 4) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Bounding box (second argument) needs to have exactly 4 elements");
if (bounding_box_num_elements < 4) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Bounding box (second argument) needs to have at least 4 elements");
return;
}
@ -158,16 +146,30 @@ PHP_METHOD(FaceLandmarkDetection, detect)
// Each key is one part from shape. Value of each part is associative array of keys "x" and "y".
//
array_init(return_value);
zval rect_arr, parts_arr;
array_init(&rect_arr);
array_init(&parts_arr);
for (int i = 0; i < shape.num_parts(); i++) {
zval part;
array_init(&part);
dlib::point p = shape.part(i);
add_assoc_long(&part, "x", p.x());
add_assoc_long(&part, "y", p.y());
add_next_index_zval(return_value, &part);
add_next_index_zval(&parts_arr, &part);
}
const rectangle& r = shape.get_rect();
add_assoc_long(&rect_arr, "left", r.left());
add_assoc_long(&rect_arr, "top", r.top());
add_assoc_long(&rect_arr, "right", r.right());
add_assoc_long(&rect_arr, "bottom", r.bottom());
add_assoc_zval(return_value, "rect", &rect_arr);
add_assoc_zval(return_value, "parts", &parts_arr);
} catch (exception& e) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
return;
}
}
}

189
src/face_recognition.cc Normal file
View File

@ -0,0 +1,189 @@
#include "../php_pdlib.h"
#include "face_recognition.h"
#include <zend_exceptions.h>
#include <dlib/image_io.h>
using namespace std;
using namespace dlib;
static inline face_recognition *php_face_recognition_from_obj(zend_object *obj) {
return (face_recognition*)((char*)(obj) - XtOffsetOf(face_recognition, std));
}
#define Z_FACE_RECOGNITION_P(zv) php_face_recognition_from_obj(Z_OBJ_P((zv)))
PHP_METHOD(FaceRecognition, __construct)
{
char *sz_face_recognition_model_path;
size_t face_recognition_model_path_len;
face_recognition *fr = Z_FACE_RECOGNITION_P(getThis());
if (NULL == fr) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unable to find obj in FaceRecognition::__construct()");
return;
}
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
&sz_face_recognition_model_path, &face_recognition_model_path_len) == FAILURE){
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Unable to parse face_recognition_model_path");
return;
}
try {
string face_recognition_model_path(sz_face_recognition_model_path, face_recognition_model_path_len);
fr->net = new anet_type;
deserialize(face_recognition_model_path) >> *(fr->net);
} catch (exception& e) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
return;
}
}
std::vector<matrix<rgb_pixel>> pdlib_jitter_image(
const matrix<rgb_pixel>& img,
const int num_jitters,
dlib::rand& rnd) {
std::vector<matrix<rgb_pixel>> crops;
for (int i = 0; i < num_jitters; ++i)
crops.push_back(dlib::jitter_image(img,rnd));
return crops;
}
// Helper macro to automatically have parsing of "top"/"bottom"/"left"/"right"
//
#define PARSE_BOUNDING_BOX_EDGE(side) \
PARSE_LONG_FROM_ARRAY(rect_hash, side, \
"Shape's rect array is missing " #side "key", "Shape's rect array's " #side " key is not long type")
// Helper macro to parse "x"/"y"
//
#define PARSE_POINT(coord) \
PARSE_LONG_FROM_ARRAY(part_hash, coord, \
#coord " coordinate key is missing in parts array", #coord " coordinate key is not of long type")
PHP_METHOD(FaceRecognition, computeDescriptor)
{
char *img_path;
size_t img_path_len;
zval *shape;
long num_jitters = 1;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa|l", &img_path, &img_path_len, &shape, &num_jitters) == FAILURE){
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Unable to parse computeDescriptor arguments");
return;
}
HashTable *shape_hash = Z_ARRVAL_P(shape);
uint32_t shape_hash_num_elements = zend_hash_num_elements(shape_hash);
if (shape_hash_num_elements != 2) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Shape (second argument) needs to have exactly 2 elements - keys \"rect\" and \"parts\"");
return;
}
zval *rect_zval = zend_hash_str_find(shape_hash, "rect", sizeof("rect")-1);
if (rect_zval == nullptr) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Shape (second argument) array needs to have \"rect\" key"); \
return;
}
if (Z_TYPE_P(rect_zval) != IS_ARRAY) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Value of shape's key \"rect\" must be array");
return;
}
HashTable *rect_hash = Z_ARRVAL_P(rect_zval);
PARSE_BOUNDING_BOX_EDGE(top)
PARSE_BOUNDING_BOX_EDGE(bottom)
PARSE_BOUNDING_BOX_EDGE(left)
PARSE_BOUNDING_BOX_EDGE(right)
rectangle rect(left, top, right, bottom);
zval *parts_zval = zend_hash_str_find(shape_hash, "parts", sizeof("parts")-1);
if (parts_zval == nullptr) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Shape (second argument) array needs to have \"parts\" key"); \
return;
}
if (Z_TYPE_P(parts_zval) != IS_ARRAY) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Value of shape's key \"parts\" must be array");
return;
}
HashTable *parts_hash = Z_ARRVAL_P(parts_zval);
HashPosition parts_pos;
uint32_t parts_count = zend_hash_num_elements(parts_hash);
point parts_points[parts_count];
if ((parts_count != 5) && (parts_count != 68)) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC,
"The full_object_detection must use the iBUG 300W 68 point face landmark style or dlib's 5 point style");
return;
}
for (zend_hash_internal_pointer_reset_ex(parts_hash, &parts_pos);
zend_hash_has_more_elements_ex(parts_hash, &parts_pos) == SUCCESS;
zend_hash_move_forward_ex(parts_hash, &parts_pos)
) {
zend_string* str_index = {0};
zend_ulong num_index;
zval *part_zval = zend_hash_get_current_data_ex(parts_hash, &parts_pos);
switch (zend_hash_get_current_key_ex(parts_hash, &str_index, &num_index, &parts_pos)) {
case HASH_KEY_IS_LONG:
if (Z_TYPE_P(part_zval) == IS_ARRAY)
{
HashTable *part_hash = Z_ARRVAL_P(part_zval);
PARSE_POINT(x)
PARSE_POINT(y)
if (num_index > parts_count) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Internal error, bad parsing of parts array");
return;
}
parts_points[num_index] = point(x, y);
} else {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Values from parts array must be arrays with \"x\" and \"y\" keys");
return;
}
break;
case HASH_KEY_IS_STRING:
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, "Parts array must be indexed and it contains string keys");
return;
break;
}
}
std::vector<point> parts;
for (unsigned int i = 0; i < parts_count; i++) {
parts.push_back(parts_points[i]);
}
try {
face_recognition *fr = Z_FACE_RECOGNITION_P(getThis());
full_object_detection fod(rect, parts);
matrix<rgb_pixel> img;
load_image(img, img_path);
std::vector<chip_details> dets;
dets.push_back(get_face_chip_details(fod, 150, 0.25));
dlib::array<matrix<rgb_pixel>> face_chips;
extract_image_chips(img, dets, face_chips);
array_init(return_value);
matrix<float,0,1> face_descriptor;
if (num_jitters <= 1) {
std::vector<matrix<float,0,1>> face_descriptors = fr->net->operator()(face_chips, 16);
face_descriptor = face_descriptors[0];
} else {
matrix<rgb_pixel>& face_chip = face_chips[0];
face_descriptor = mean(mat(fr->net->operator()(pdlib_jitter_image(face_chip, num_jitters, fr->rnd), 16)));
}
for (auto& d : face_descriptor) {
add_next_index_double(return_value, d);
}
} catch (exception& e) {
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
return;
}
}

58
src/face_recognition.h Normal file
View File

@ -0,0 +1,58 @@
//
// Created by branko at kokanovic dot org on 2018/8/26.
//
#ifndef PHP_DLIB_FACE_RECOGNITION_H
#define PHP_DLIB_FACE_RECOGNITION_H
#include <dlib/dnn.h>
using namespace dlib;
template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = add_prev1<block<N,BN,1,tag1<SUBNET>>>;
template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = add_prev2<avg_pool<2,2,2,2,skip1<tag2<block<N,BN,2,tag1<SUBNET>>>>>>;
template <int N, template <typename> class BN, int stride, typename SUBNET>
using block = BN<con<N,3,3,1,1,relu<BN<con<N,3,3,stride,stride,SUBNET>>>>>;
template <int N, typename SUBNET> using ares = relu<residual<block,N,affine,SUBNET>>;
template <int N, typename SUBNET> using ares_down = relu<residual_down<block,N,affine,SUBNET>>;
template <typename SUBNET> using alevel0 = ares_down<256,SUBNET>;
template <typename SUBNET> using alevel1 = ares<256,ares<256,ares_down<256,SUBNET>>>;
template <typename SUBNET> using alevel2 = ares<128,ares<128,ares_down<128,SUBNET>>>;
template <typename SUBNET> using alevel3 = ares<64,ares<64,ares<64,ares_down<64,SUBNET>>>>;
template <typename SUBNET> using alevel4 = ares<32,ares<32,ares<32,SUBNET>>>;
using anet_type = loss_metric<fc_no_bias<128,avg_pool_everything<
alevel0<
alevel1<
alevel2<
alevel3<
alevel4<
max_pool<3,3,2,2,relu<affine<con<32,7,7,2,2,
input_rgb_image_sized<150>
>>>>>>>>>>>>;
typedef struct _face_recognition {
anet_type *net;
zend_object std;
dlib::rand rnd;
} face_recognition;
ZEND_BEGIN_ARG_INFO_EX(face_recognition_ctor_arginfo, 0, 0, 1)
ZEND_ARG_INFO(0, face_recognition_model_path)
ZEND_END_ARG_INFO()
PHP_METHOD(FaceRecognition, __construct);
ZEND_BEGIN_ARG_INFO_EX(face_recognition_compute_descriptor_arginfo, 0, 0, 3)
ZEND_ARG_INFO(0, img_path)
ZEND_ARG_INFO(0, landmarks)
ZEND_ARG_INFO(0, num_jitters)
ZEND_END_ARG_INFO()
PHP_METHOD(FaceRecognition, computeDescriptor);
#endif //PHP_DLIB_FACE_RECOGNITION_H

View File

@ -0,0 +1,15 @@
--TEST--
Testing FaceRecognition constructor without arguments
--SKIPIF--
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
--FILE--
<?php
try {
new FaceRecognition();
} catch (Exception $e) {
var_dump($e->getMessage());
}
?>
--EXPECT--
Warning: FaceRecognition::__construct() expects exactly 1 parameter, 0 given in /home/branko/pdlib/tests/face_recognition_ctor_error.php on line 3
string(43) "Unable to parse face_recognition_model_path"

View File

@ -0,0 +1,60 @@
--TEST--
Full test for face recognition - download models, detect faces, landmark detection and face recognition.
--SKIPIF--
<?php if (!extension_loaded("pdlib") || (function_exists("bzopen"))) print "skip"; ?>
--FILE--
<?php
$models = array(
"detection" => array("uri"=>"http://dlib.net/files/mmod_human_face_detector.dat.bz2"),
"prediction" => array("uri"=>"http://dlib.net/files/shape_predictor_5_face_landmarks.dat.bz2"),
"recognition" => array("uri"=>"http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2")
);
// Check if there are models in local tmp. Download them if not (lazy caching).
//
foreach ($models as $modelName => $modelBag) {
printf("Processing %s model\n", $modelName);
$bz2_filename = array_values(array_slice(explode("/", $modelBag["uri"]), -1))[0];
$temp_bz2_file = sys_get_temp_dir() . "/" . $bz2_filename;
$dat_filename = array_values(array_slice(explode(".", $bz2_filename), 0))[0] . ".dat";
$temp_dat_file = sys_get_temp_dir() . "/" . $dat_filename;
$models[$modelName]["local_path"] = $temp_dat_file;
if (file_exists($temp_dat_file)) {
continue;
}
file_put_contents($temp_bz2_file, fopen($modelBag["uri"], 'r'));
$bz = bzopen($temp_bz2_file, "r");
$decompressed_file = "";
while (!feof($bz)) {
$decompressed_file .= bzread($bz, 4096);
}
bzclose($bz);
file_put_contents($temp_dat_file, $decompressed_file);
}
printf("Detection\n");
$fd = new CnnFaceDetection($models["detection"]["local_path"]);
$detected_faces = $fd->detect(__DIR__ . "/lenna.jpg");
printf("Faces found = %d\n", count($detected_faces));
foreach($detected_faces as $index => $detected_face) {
printf("Face[%d] in bounding box (left=%d, top=%d, right=%d, bottom=%d)\n", $index,
$detected_face["left"], $detected_face["top"], $detected_face["right"], $detected_face["bottom"]);
$fld = new FaceLandmarkDetection($models["prediction"]["local_path"]);
$landmarks = $fld->detect(__DIR__ . "/lenna.jpg", $detected_face);
printf("Since we used model with 5 shape predictions, we found %d landmark parts\n", count($landmarks["parts"]));
$fr = new FaceRecognition($models["recognition"]["local_path"]);
$descriptor = $fr->computeDescriptor(__DIR__ . "/lenna.jpg", $landmarks);
printf("Descriptor is vector of %d dimensions\n", count($descriptor));
}
?>
--EXPECT--
Processing detection model
Processing prediction model
Processing recognition model
Detection
Faces found = 1
Face[0] in bounding box (left=187, top=186, right=357, bottom=355)
Since we used model with 5 shape predictions, we found 5 landmark parts
Descriptor is vector of 128 dimensions

BIN
tests/lenna.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB