From 14b1ae350b1c6a1da50f79d040417be7384d96a3 Mon Sep 17 00:00:00 2001 From: Branko Kokanovic Date: Thu, 19 Jul 2018 19:12:18 +0200 Subject: [PATCH] Support for raw chinese_whispers This commit introduces chinese_whispers method without any "helpers" on PHP side. Users needs to take care of everything (building edges from 128D face chip vector, for example), but this is exposed for people that need low-level call and want to calculate distances and build edges in PHP directly. --- README.md | 19 ++++ config.m4 | 1 + pdlib.cc | 2 + src/chinese_whispers.cc | 104 ++++++++++++++++++ src/chinese_whispers.h | 13 +++ tests/chinese_whispers_basic.phpt | 27 +++++ ...whispers_edge_associative_array_error.phpt | 13 +++ ...inese_whispers_edge_elements_not_long.phpt | 19 ++++ ...ese_whispers_edge_not_2_element_error.phpt | 20 ++++ ...chinese_whispers_edge_not_array_error.phpt | 21 ++++ ...chinese_whispers_wrong_arg_type_error.phpt | 15 +++ 11 files changed, 254 insertions(+) create mode 100644 src/chinese_whispers.cc create mode 100644 src/chinese_whispers.h create mode 100644 tests/chinese_whispers_basic.phpt create mode 100644 tests/chinese_whispers_edge_associative_array_error.phpt create mode 100644 tests/chinese_whispers_edge_elements_not_long.phpt create mode 100644 tests/chinese_whispers_edge_not_2_element_error.phpt create mode 100644 tests/chinese_whispers_edge_not_array_error.phpt create mode 100644 tests/chinese_whispers_wrong_arg_type_error.phpt diff --git a/README.md b/README.md index ad3114f..bb75092 100644 --- a/README.md +++ b/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 + +#include +#include + +using namespace dlib; +using namespace std; + +PHP_FUNCTION(dlib_chinese_whispers) +{ + zval *edges_arg; + HashTable *edges_arg_hash; + + std::vector edges; + std::vector 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; + } +} + diff --git a/src/chinese_whispers.h b/src/chinese_whispers.h new file mode 100644 index 0000000..ec38e62 --- /dev/null +++ b/src/chinese_whispers.h @@ -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 diff --git a/tests/chinese_whispers_basic.phpt b/tests/chinese_whispers_basic.phpt new file mode 100644 index 0000000..5f8e58d --- /dev/null +++ b/tests/chinese_whispers_basic.phpt @@ -0,0 +1,27 @@ +--TEST-- +Basic tests for chinese_whispers +--SKIPIF-- + +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(0) +} +array(2) { + [0]=> + int(0) + [1]=> + int(1) +} +array(2) { + [0]=> + int(0) + [1]=> + int(0) +} diff --git a/tests/chinese_whispers_edge_associative_array_error.phpt b/tests/chinese_whispers_edge_associative_array_error.phpt new file mode 100644 index 0000000..ac7cd1d --- /dev/null +++ b/tests/chinese_whispers_edge_associative_array_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Edge given in edges array for chinese_whispers functions is associative array +--SKIPIF-- + +--FILE-- +0, "bar"=>1]]); +} catch (Exception $e) { + var_dump($e->getMessage()); +} +--EXPECT-- +string(46) "Edge should be numeric array with integer keys" diff --git a/tests/chinese_whispers_edge_elements_not_long.phpt b/tests/chinese_whispers_edge_elements_not_long.phpt new file mode 100644 index 0000000..9b06728 --- /dev/null +++ b/tests/chinese_whispers_edge_elements_not_long.phpt @@ -0,0 +1,19 @@ +--TEST-- +Edge elements given in edges array for chinese_whispers functions are not of long type +--SKIPIF-- + +--FILE-- +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" diff --git a/tests/chinese_whispers_edge_not_2_element_error.phpt b/tests/chinese_whispers_edge_not_2_element_error.phpt new file mode 100644 index 0000000..d46de75 --- /dev/null +++ b/tests/chinese_whispers_edge_not_2_element_error.phpt @@ -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-- + +--FILE-- +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" diff --git a/tests/chinese_whispers_edge_not_array_error.phpt b/tests/chinese_whispers_edge_not_array_error.phpt new file mode 100644 index 0000000..01c4a8c --- /dev/null +++ b/tests/chinese_whispers_edge_not_array_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +Edge given in edges array is not array for chinese_whispers functions +--SKIPIF-- + +--FILE-- +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" diff --git a/tests/chinese_whispers_wrong_arg_type_error.phpt b/tests/chinese_whispers_wrong_arg_type_error.phpt new file mode 100644 index 0000000..15fb21a --- /dev/null +++ b/tests/chinese_whispers_wrong_arg_type_error.phpt @@ -0,0 +1,15 @@ +--TEST-- +Args given to chinese_whispers functions is not correct +--SKIPIF-- + +--FILE-- +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"