Merge pull request #4 from stalker314314/chinese_whispers
Support for raw chinese_whispers
This commit is contained in:
commit
091caba0d8
19
README.md
19
README.md
@ -71,9 +71,28 @@ var_dump($landmarks);
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### chinese whisers
|
||||||
|
|
||||||
|
Provides raw access to dlib's `chinese_whispers` function.
|
||||||
|
Client need to build and provide edges. Edges are provided
|
||||||
|
as numeric array. Each element of this array should also be
|
||||||
|
numeric array with 2 elements of long type.
|
||||||
|
|
||||||
|
Returned value is also numeric array, containing obtained labels.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// This example will cluster nodes 0 and 1, but would leave 2 out.
|
||||||
|
// $labels will look like [0,0,1].
|
||||||
|
$edges = [[0,0], [0,1], [1,1], [2,2]];
|
||||||
|
$labels = dlib_chinese_whispers($edges);
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [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
|
||||||
- [x] 4.Deep Learning Face Detection
|
- [x] 4.Deep Learning Face Detection
|
||||||
|
- [x] 5. Raw chinese_whispers
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ if test "$PHP_PDLIB" != "no"; then
|
|||||||
PHP_SUBST(PDLIB_SHARED_LIBADD)
|
PHP_SUBST(PDLIB_SHARED_LIBADD)
|
||||||
|
|
||||||
pdlib_src_files="pdlib.cc \
|
pdlib_src_files="pdlib.cc \
|
||||||
|
src/chinese_whispers.cc \
|
||||||
src/face_detection.cc \
|
src/face_detection.cc \
|
||||||
src/face_landmark_detection.cc \
|
src/face_landmark_detection.cc \
|
||||||
src/cnn_face_detection.cc"
|
src/cnn_face_detection.cc"
|
||||||
|
2
pdlib.cc
2
pdlib.cc
@ -28,6 +28,7 @@ extern "C" {
|
|||||||
#include "ext/standard/info.h"
|
#include "ext/standard/info.h"
|
||||||
}
|
}
|
||||||
#include "php_pdlib.h"
|
#include "php_pdlib.h"
|
||||||
|
#include "src/chinese_whispers.h"
|
||||||
#include "src/face_detection.h"
|
#include "src/face_detection.h"
|
||||||
#include "src/cnn_face_detection.h"
|
#include "src/cnn_face_detection.h"
|
||||||
#include "src/face_landmark_detection.h"
|
#include "src/face_landmark_detection.h"
|
||||||
@ -187,6 +188,7 @@ PHP_MINFO_FUNCTION(pdlib)
|
|||||||
*/
|
*/
|
||||||
const zend_function_entry pdlib_functions[] = {
|
const zend_function_entry pdlib_functions[] = {
|
||||||
PHP_FE(confirm_pdlib_compiled, NULL)
|
PHP_FE(confirm_pdlib_compiled, NULL)
|
||||||
|
PHP_FE(dlib_chinese_whispers, dlib_chinese_whispers_arginfo)
|
||||||
PHP_FE(dlib_face_detection, dlib_face_detection_arginfo)
|
PHP_FE(dlib_face_detection, dlib_face_detection_arginfo)
|
||||||
PHP_FE(dlib_face_landmark_detection, dlib_face_landmark_detection_arginfo)
|
PHP_FE(dlib_face_landmark_detection, dlib_face_landmark_detection_arginfo)
|
||||||
PHP_FE_END /* Must be the last line in pdlib_functions[] */
|
PHP_FE_END /* Must be the last line in pdlib_functions[] */
|
||||||
|
104
src/chinese_whispers.cc
Normal file
104
src/chinese_whispers.cc
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
|
||||||
|
#include "../php_pdlib.h"
|
||||||
|
#include "chinese_whispers.h"
|
||||||
|
|
||||||
|
#include <zend_exceptions.h>
|
||||||
|
#include <dlib/clustering.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace dlib;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
PHP_FUNCTION(dlib_chinese_whispers)
|
||||||
|
{
|
||||||
|
zval *edges_arg;
|
||||||
|
HashTable *edges_arg_hash;
|
||||||
|
|
||||||
|
std::vector<sample_pair> edges;
|
||||||
|
std::vector<unsigned long> labels;
|
||||||
|
|
||||||
|
if(zend_parse_parameters(ZEND_NUM_ARGS(), "a", &edges_arg) == FAILURE){
|
||||||
|
zend_throw_exception_ex(
|
||||||
|
zend_ce_exception,
|
||||||
|
0 TSRMLS_CC,
|
||||||
|
"Unable to parse edges in dlib_chinese_whispers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
edges_arg_hash = Z_ARRVAL_P(edges_arg);
|
||||||
|
|
||||||
|
try {
|
||||||
|
HashPosition pos;
|
||||||
|
zval *edge;
|
||||||
|
HashTable *edge_hash;
|
||||||
|
|
||||||
|
// Iterate for all given edges, check if they are valid and put them to edges
|
||||||
|
//
|
||||||
|
for (
|
||||||
|
zend_hash_internal_pointer_reset_ex(edges_arg_hash, &pos);
|
||||||
|
(edge = zend_hash_get_current_data_ex(edges_arg_hash, &pos)) != nullptr;
|
||||||
|
zend_hash_move_forward_ex(edges_arg_hash, &pos)) {
|
||||||
|
// Check that each given edge is actually array
|
||||||
|
//
|
||||||
|
if (Z_TYPE_P(edge) != IS_ARRAY) {
|
||||||
|
zend_throw_exception_ex(
|
||||||
|
zend_ce_exception,
|
||||||
|
0 TSRMLS_CC,
|
||||||
|
"Each edge provided in array needs to be numeric array of 2 elements");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
edge_hash = Z_ARRVAL_P(edge);
|
||||||
|
|
||||||
|
// Check that there are two elements in this edge
|
||||||
|
//
|
||||||
|
if (zend_hash_num_elements(edge_hash) != 2) {
|
||||||
|
zend_throw_exception_ex(
|
||||||
|
zend_ce_exception,
|
||||||
|
0 TSRMLS_CC,
|
||||||
|
"Edges need to contain exactly two elements");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that this is regular array with integer keys
|
||||||
|
//
|
||||||
|
if (!zend_hash_index_exists(edge_hash, 0) ||
|
||||||
|
!zend_hash_index_exists(edge_hash, 1)) {
|
||||||
|
zend_throw_exception_ex(
|
||||||
|
zend_ce_exception,
|
||||||
|
0 TSRMLS_CC,
|
||||||
|
"Edge should be numeric array with integer keys");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zval *elem_i = zend_hash_index_find(edge_hash, 0);
|
||||||
|
zval *elem_j = zend_hash_index_find(edge_hash, 1);
|
||||||
|
|
||||||
|
// Check that both elements in array are longs
|
||||||
|
if ((Z_TYPE_P(elem_i) != IS_LONG) || (Z_TYPE_P(elem_j) != IS_LONG)) {
|
||||||
|
zend_throw_exception_ex(
|
||||||
|
zend_ce_exception,
|
||||||
|
0 TSRMLS_CC,
|
||||||
|
"Both elements in each edge must be of long type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, put extracted elements to edges
|
||||||
|
edges.push_back(sample_pair(Z_LVAL_P(elem_i), Z_LVAL_P(elem_j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
chinese_whispers(edges, labels);
|
||||||
|
|
||||||
|
// Preparing and generating response array containing labels
|
||||||
|
//
|
||||||
|
array_init(return_value);
|
||||||
|
for (auto label = labels.begin(); label != labels.end(); label++) {
|
||||||
|
add_next_index_long(return_value, *label);
|
||||||
|
}
|
||||||
|
} catch (exception& e)
|
||||||
|
{
|
||||||
|
zend_throw_exception_ex(zend_ce_exception, 0 TSRMLS_CC, e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
src/chinese_whispers.h
Normal file
13
src/chinese_whispers.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Created by branko at kokanovic dot org on 2018/7/19.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef PHP_DLIB_CHINESE_WHISPERS_H
|
||||||
|
#define PHP_DLIB_CHINESE_WHISPERS_H
|
||||||
|
|
||||||
|
ZEND_BEGIN_ARG_INFO_EX(dlib_chinese_whispers_arginfo, 0, 0, 1)
|
||||||
|
ZEND_ARG_INFO(0, edges)
|
||||||
|
ZEND_END_ARG_INFO()
|
||||||
|
PHP_FUNCTION(dlib_chinese_whispers);
|
||||||
|
|
||||||
|
#endif //PHP_DLIB_CHINESE_WHISPERS_H
|
27
tests/chinese_whispers_basic.phpt
Normal file
27
tests/chinese_whispers_basic.phpt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
--TEST--
|
||||||
|
Basic tests for chinese_whispers
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
var_dump(dlib_chinese_whispers([[0,0]])); // Case with only one node
|
||||||
|
var_dump(dlib_chinese_whispers([[0,0], [1,1]])); // Case with two separate nodes
|
||||||
|
var_dump(dlib_chinese_whispers([[0,0], [0,1], [1,0], [1,1]])); // Case with two connected nodes
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
array(1) {
|
||||||
|
[0]=>
|
||||||
|
int(0)
|
||||||
|
}
|
||||||
|
array(2) {
|
||||||
|
[0]=>
|
||||||
|
int(0)
|
||||||
|
[1]=>
|
||||||
|
int(1)
|
||||||
|
}
|
||||||
|
array(2) {
|
||||||
|
[0]=>
|
||||||
|
int(0)
|
||||||
|
[1]=>
|
||||||
|
int(0)
|
||||||
|
}
|
13
tests/chinese_whispers_edge_associative_array_error.phpt
Normal file
13
tests/chinese_whispers_edge_associative_array_error.phpt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
--TEST--
|
||||||
|
Edge given in edges array for chinese_whispers functions is associative array
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], ["foo"=>0, "bar"=>1]]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
--EXPECT--
|
||||||
|
string(46) "Edge should be numeric array with integer keys"
|
19
tests/chinese_whispers_edge_elements_not_long.phpt
Normal file
19
tests/chinese_whispers_edge_elements_not_long.phpt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
--TEST--
|
||||||
|
Edge elements given in edges array for chinese_whispers functions are not of long type
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], [1, "foo"]]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], [1, 1.1]]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
--EXPECT--
|
||||||
|
string(47) "Both elements in each edge must be of long type"
|
||||||
|
string(47) "Both elements in each edge must be of long type"
|
20
tests/chinese_whispers_edge_not_2_element_error.phpt
Normal file
20
tests/chinese_whispers_edge_not_2_element_error.phpt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
--TEST--
|
||||||
|
Edge given in edges array for chinese_whispers functions is not having all values to be arrays with 2 elements
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], [1]]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], [1,1,1]]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(42) "Edges need to contain exactly two elements"
|
||||||
|
string(42) "Edges need to contain exactly two elements"
|
21
tests/chinese_whispers_edge_not_array_error.phpt
Normal file
21
tests/chinese_whispers_edge_not_array_error.phpt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
--TEST--
|
||||||
|
Edge given in edges array is not array for chinese_whispers functions
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], "foo"]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers([[0,0], 1]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(67) "Each edge provided in array needs to be numeric array of 2 elements"
|
||||||
|
string(67) "Each edge provided in array needs to be numeric array of 2 elements"
|
15
tests/chinese_whispers_wrong_arg_type_error.phpt
Normal file
15
tests/chinese_whispers_wrong_arg_type_error.phpt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
--TEST--
|
||||||
|
Args given to chinese_whispers functions is not correct
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("pdlib")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
try {
|
||||||
|
dlib_chinese_whispers("foo");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
var_dump($e->getMessage());
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Warning: dlib_chinese_whispers() expects parameter 1 to be array, string given in /home/branko/pdlib/tests/chinese_whispers_wrong_arg_type_error.php on line 3
|
||||||
|
string(46) "Unable to parse edges in dlib_chinese_whispers"
|
Loading…
Reference in New Issue
Block a user