From c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:22 +0100 Subject: Adding upstream version 1.44.3. Signed-off-by: Daniel Baumann --- ml/dlib/tools/python/src/basic.cpp | 272 +++++++ ml/dlib/tools/python/src/cca.cpp | 137 ++++ ml/dlib/tools/python/src/cnn_face_detector.cpp | 183 +++++ ml/dlib/tools/python/src/conversion.h | 52 ++ ml/dlib/tools/python/src/correlation_tracker.cpp | 167 +++++ ml/dlib/tools/python/src/decision_functions.cpp | 263 +++++++ ml/dlib/tools/python/src/dlib.cpp | 110 +++ ml/dlib/tools/python/src/face_recognition.cpp | 245 ++++++ ml/dlib/tools/python/src/global_optimization.cpp | 442 +++++++++++ ml/dlib/tools/python/src/gui.cpp | 128 ++++ ml/dlib/tools/python/src/image.cpp | 40 + .../tools/python/src/image_dataset_metadata.cpp | 279 +++++++ ml/dlib/tools/python/src/indexing.h | 11 + ml/dlib/tools/python/src/matrix.cpp | 209 ++++++ ml/dlib/tools/python/src/numpy_returns.cpp | 158 ++++ ml/dlib/tools/python/src/numpy_returns_stub.cpp | 59 ++ ml/dlib/tools/python/src/object_detection.cpp | 376 ++++++++++ ml/dlib/tools/python/src/opaque_types.h | 55 ++ ml/dlib/tools/python/src/other.cpp | 268 +++++++ ml/dlib/tools/python/src/rectangles.cpp | 268 +++++++ ml/dlib/tools/python/src/sequence_segmenter.cpp | 827 +++++++++++++++++++++ .../tools/python/src/serialize_object_detector.h | 49 ++ ml/dlib/tools/python/src/shape_predictor.cpp | 319 ++++++++ ml/dlib/tools/python/src/shape_predictor.h | 259 +++++++ ml/dlib/tools/python/src/simple_object_detector.h | 318 ++++++++ .../tools/python/src/simple_object_detector_py.h | 290 ++++++++ ml/dlib/tools/python/src/svm_c_trainer.cpp | 311 ++++++++ ml/dlib/tools/python/src/svm_rank_trainer.cpp | 161 ++++ ml/dlib/tools/python/src/svm_struct.cpp | 151 ++++ ml/dlib/tools/python/src/testing_results.h | 50 ++ ml/dlib/tools/python/src/vector.cpp | 182 +++++ 31 files changed, 6639 insertions(+) create mode 100644 ml/dlib/tools/python/src/basic.cpp create mode 100644 ml/dlib/tools/python/src/cca.cpp create mode 100644 ml/dlib/tools/python/src/cnn_face_detector.cpp create mode 100644 ml/dlib/tools/python/src/conversion.h create mode 100644 ml/dlib/tools/python/src/correlation_tracker.cpp create mode 100644 ml/dlib/tools/python/src/decision_functions.cpp create mode 100644 ml/dlib/tools/python/src/dlib.cpp create mode 100644 ml/dlib/tools/python/src/face_recognition.cpp create mode 100644 ml/dlib/tools/python/src/global_optimization.cpp create mode 100644 ml/dlib/tools/python/src/gui.cpp create mode 100644 ml/dlib/tools/python/src/image.cpp create mode 100644 ml/dlib/tools/python/src/image_dataset_metadata.cpp create mode 100644 ml/dlib/tools/python/src/indexing.h create mode 100644 ml/dlib/tools/python/src/matrix.cpp create mode 100644 ml/dlib/tools/python/src/numpy_returns.cpp create mode 100644 ml/dlib/tools/python/src/numpy_returns_stub.cpp create mode 100644 ml/dlib/tools/python/src/object_detection.cpp create mode 100644 ml/dlib/tools/python/src/opaque_types.h create mode 100644 ml/dlib/tools/python/src/other.cpp create mode 100644 ml/dlib/tools/python/src/rectangles.cpp create mode 100644 ml/dlib/tools/python/src/sequence_segmenter.cpp create mode 100644 ml/dlib/tools/python/src/serialize_object_detector.h create mode 100644 ml/dlib/tools/python/src/shape_predictor.cpp create mode 100644 ml/dlib/tools/python/src/shape_predictor.h create mode 100644 ml/dlib/tools/python/src/simple_object_detector.h create mode 100644 ml/dlib/tools/python/src/simple_object_detector_py.h create mode 100644 ml/dlib/tools/python/src/svm_c_trainer.cpp create mode 100644 ml/dlib/tools/python/src/svm_rank_trainer.cpp create mode 100644 ml/dlib/tools/python/src/svm_struct.cpp create mode 100644 ml/dlib/tools/python/src/testing_results.h create mode 100644 ml/dlib/tools/python/src/vector.cpp (limited to 'ml/dlib/tools/python/src') diff --git a/ml/dlib/tools/python/src/basic.cpp b/ml/dlib/tools/python/src/basic.cpp new file mode 100644 index 000000000..d87a53cc3 --- /dev/null +++ b/ml/dlib/tools/python/src/basic.cpp @@ -0,0 +1,272 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#include +#include +#include +#include +#include "opaque_types.h" + +#include +#include + +using namespace std; +using namespace dlib; +namespace py = pybind11; + + +std::shared_ptr > array_from_object(py::object obj) +{ + try { + long nr = obj.cast(); + return std::make_shared>(nr); + } catch (py::cast_error &e) { + py::list li = obj.cast(); + const long nr = len(li); + auto temp = std::make_shared>(nr); + for ( long r = 0; r < nr; ++r) + { + (*temp)[r] = li[r].cast(); + } + return temp; + } +} + +string array__str__ (const std::vector& v) +{ + std::ostringstream sout; + for (unsigned long i = 0; i < v.size(); ++i) + { + sout << v[i]; + if (i+1 < v.size()) + sout << "\n"; + } + return sout.str(); +} + +string array__repr__ (const std::vector& v) +{ + std::ostringstream sout; + sout << "dlib.array(["; + for (unsigned long i = 0; i < v.size(); ++i) + { + sout << v[i]; + if (i+1 < v.size()) + sout << ", "; + } + sout << "])"; + return sout.str(); +} + +string range__str__ (const std::pair& p) +{ + std::ostringstream sout; + sout << p.first << ", " << p.second; + return sout.str(); +} + +string range__repr__ (const std::pair& p) +{ + std::ostringstream sout; + sout << "dlib.range(" << p.first << ", " << p.second << ")"; + return sout.str(); +} + +struct range_iter +{ + std::pair range; + unsigned long cur; + + unsigned long next() + { + if (cur < range.second) + { + return cur++; + } + else + { + PyErr_SetString(PyExc_StopIteration, "No more data."); + throw py::error_already_set(); + } + } +}; + +range_iter make_range_iterator (const std::pair& p) +{ + range_iter temp; + temp.range = p; + temp.cur = p.first; + return temp; +} + +string pair__str__ (const std::pair& p) +{ + std::ostringstream sout; + sout << p.first << ": " << p.second; + return sout.str(); +} + +string pair__repr__ (const std::pair& p) +{ + std::ostringstream sout; + sout << "dlib.pair(" << p.first << ", " << p.second << ")"; + return sout.str(); +} + +string sparse_vector__str__ (const std::vector >& v) +{ + std::ostringstream sout; + for (unsigned long i = 0; i < v.size(); ++i) + { + sout << v[i].first << ": " << v[i].second; + if (i+1 < v.size()) + sout << "\n"; + } + return sout.str(); +} + +string sparse_vector__repr__ (const std::vector >& v) +{ + std::ostringstream sout; + sout << "< dlib.sparse_vector containing: \n" << sparse_vector__str__(v) << " >"; + return sout.str(); +} + +unsigned long range_len(const std::pair& r) +{ + if (r.second > r.first) + return r.second-r.first; + else + return 0; +} + +template +void resize(T& v, unsigned long n) { v.resize(n); } + +void bind_basic_types(py::module& m) +{ + { + typedef double item_type; + typedef std::vector type; + typedef std::shared_ptr type_ptr; + py::bind_vector(m, "array", "This object represents a 1D array of floating point numbers. " + "Moreover, it binds directly to the C++ type std::vector.") + .def(py::init(&array_from_object)) + .def("__str__", array__str__) + .def("__repr__", array__repr__) + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + { + typedef matrix item_type; + typedef std::vector type; + py::bind_vector(m, "vectors", "This object is an array of vector objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + { + typedef std::vector > item_type; + typedef std::vector type; + py::bind_vector(m, "vectorss", "This object is an array of arrays of vector objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + typedef pair range_type; + py::class_(m, "range", "This object is used to represent a range of elements in an array.") + .def(py::init()) + .def_readwrite("begin",&range_type::first, "The index of the first element in the range. This is represented using an unsigned integer.") + .def_readwrite("end",&range_type::second, "One past the index of the last element in the range. This is represented using an unsigned integer.") + .def("__str__", range__str__) + .def("__repr__", range__repr__) + .def("__iter__", &make_range_iterator) + .def("__len__", &range_len) + .def(py::pickle(&getstate, &setstate)); + + py::class_(m, "_range_iter") + .def("next", &range_iter::next) + .def("__next__", &range_iter::next); + + { + typedef std::pair item_type; + typedef std::vector type; + py::bind_vector(m, "ranges", "This object is an array of range objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + { + typedef std::vector > item_type; + typedef std::vector type; + py::bind_vector(m, "rangess", "This object is an array of arrays of range objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + + typedef pair pair_type; + py::class_(m, "pair", "This object is used to represent the elements of a sparse_vector.") + .def(py::init()) + .def_readwrite("first",&pair_type::first, "This field represents the index/dimension number.") + .def_readwrite("second",&pair_type::second, "This field contains the value in a vector at dimension specified by the first field.") + .def("__str__", pair__str__) + .def("__repr__", pair__repr__) + .def(py::pickle(&getstate, &setstate)); + + { + typedef std::vector type; + py::bind_vector(m, "sparse_vector", +"This object represents the mathematical idea of a sparse column vector. It is \n\ +simply an array of dlib.pair objects, each representing an index/value pair in \n\ +the vector. Any elements of the vector which are missing are implicitly set to \n\ +zero. \n\ + \n\ +Unless otherwise noted, any routines taking a sparse_vector assume the sparse \n\ +vector is sorted and has unique elements. That is, the index values of the \n\ +pairs in a sparse_vector should be listed in increasing order and there should \n\ +not be duplicates. However, some functions work with \"unsorted\" sparse \n\ +vectors. These are dlib.sparse_vector objects that have either duplicate \n\ +entries or non-sorted index values. Note further that you can convert an \n\ +\"unsorted\" sparse_vector into a properly sorted sparse vector by calling \n\ +dlib.make_sparse_vector() on it. " + ) + .def("__str__", sparse_vector__str__) + .def("__repr__", sparse_vector__repr__) + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + { + typedef std::vector item_type; + typedef std::vector type; + py::bind_vector(m, "sparse_vectors", "This object is an array of sparse_vector objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } + + { + typedef std::vector > item_type; + typedef std::vector type; + py::bind_vector(m, "sparse_vectorss", "This object is an array of arrays of sparse_vector objects.") + .def("clear", &type::clear) + .def("resize", resize) + .def("extend", extend_vector_with_python_list) + .def(py::pickle(&getstate, &setstate)); + } +} + diff --git a/ml/dlib/tools/python/src/cca.cpp b/ml/dlib/tools/python/src/cca.cpp new file mode 100644 index 000000000..dcf476522 --- /dev/null +++ b/ml/dlib/tools/python/src/cca.cpp @@ -0,0 +1,137 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include + +using namespace dlib; +namespace py = pybind11; + +typedef std::vector > sparse_vect; + +struct cca_outputs +{ + matrix correlations; + matrix Ltrans; + matrix Rtrans; +}; + +cca_outputs _cca1 ( + const std::vector& L, + const std::vector& R, + unsigned long num_correlations, + unsigned long extra_rank, + unsigned long q, + double regularization +) +{ + pyassert(num_correlations > 0 && L.size() > 0 && R.size() > 0 && L.size() == R.size() && regularization >= 0, + "Invalid inputs"); + + cca_outputs temp; + temp.correlations = cca(L,R,temp.Ltrans,temp.Rtrans,num_correlations,extra_rank,q,regularization); + return temp; +} + +// ---------------------------------------------------------------------------------------- + +unsigned long sparse_vector_max_index_plus_one ( + const sparse_vect& v +) +{ + return max_index_plus_one(v); +} + +matrix apply_cca_transform ( + const matrix& m, + const sparse_vect& v +) +{ + pyassert((long)max_index_plus_one(v) <= m.nr(), "Invalid Inputs"); + return sparse_matrix_vector_multiply(trans(m), v); +} + +void bind_cca(py::module& m) +{ + py::class_(m, "cca_outputs") + .def_readwrite("correlations", &cca_outputs::correlations) + .def_readwrite("Ltrans", &cca_outputs::Ltrans) + .def_readwrite("Rtrans", &cca_outputs::Rtrans); + + m.def("max_index_plus_one", sparse_vector_max_index_plus_one, py::arg("v"), +"ensures \n\ + - returns the dimensionality of the given sparse vector. That is, returns a \n\ + number one larger than the maximum index value in the vector. If the vector \n\ + is empty then returns 0. " + ); + + + m.def("apply_cca_transform", apply_cca_transform, py::arg("m"), py::arg("v"), +"requires \n\ + - max_index_plus_one(v) <= m.nr() \n\ +ensures \n\ + - returns trans(m)*v \n\ + (i.e. multiply m by the vector v and return the result) " + ); + + + m.def("cca", _cca1, py::arg("L"), py::arg("R"), py::arg("num_correlations"), py::arg("extra_rank")=5, py::arg("q")=2, py::arg("regularization")=0, +"requires \n\ + - num_correlations > 0 \n\ + - len(L) > 0 \n\ + - len(R) > 0 \n\ + - len(L) == len(R) \n\ + - regularization >= 0 \n\ + - L and R must be properly sorted sparse vectors. This means they must list their \n\ + elements in ascending index order and not contain duplicate index values. You can use \n\ + make_sparse_vector() to ensure this is true. \n\ +ensures \n\ + - This function performs a canonical correlation analysis between the vectors \n\ + in L and R. That is, it finds two transformation matrices, Ltrans and \n\ + Rtrans, such that row vectors in the transformed matrices L*Ltrans and \n\ + R*Rtrans are as correlated as possible (note that in this notation we \n\ + interpret L as a matrix with the input vectors in its rows). Note also that \n\ + this function tries to find transformations which produce num_correlations \n\ + dimensional output vectors. \n\ + - Note that you can easily apply the transformation to a vector using \n\ + apply_cca_transform(). So for example, like this: \n\ + - apply_cca_transform(Ltrans, some_sparse_vector) \n\ + - returns a structure containing the Ltrans and Rtrans transformation matrices \n\ + as well as the estimated correlations between elements of the transformed \n\ + vectors. \n\ + - This function assumes the data vectors in L and R have already been centered \n\ + (i.e. we assume the vectors have zero means). However, in many cases it is \n\ + fine to use uncentered data with cca(). But if it is important for your \n\ + problem then you should center your data before passing it to cca(). \n\ + - This function works with reduced rank approximations of the L and R matrices. \n\ + This makes it fast when working with large matrices. In particular, we use \n\ + the dlib::svd_fast() routine to find reduced rank representations of the input \n\ + matrices by calling it as follows: svd_fast(L, U,D,V, num_correlations+extra_rank, q) \n\ + and similarly for R. This means that you can use the extra_rank and q \n\ + arguments to cca() to influence the accuracy of the reduced rank \n\ + approximation. However, the default values should work fine for most \n\ + problems. \n\ + - The dimensions of the output vectors produced by L*#Ltrans or R*#Rtrans are \n\ + ordered such that the dimensions with the highest correlations come first. \n\ + That is, after applying the transforms produced by cca() to a set of vectors \n\ + you will find that dimension 0 has the highest correlation, then dimension 1 \n\ + has the next highest, and so on. This also means that the list of estimated \n\ + correlations returned from cca() will always be listed in decreasing order. \n\ + - This function performs the ridge regression version of Canonical Correlation \n\ + Analysis when regularization is set to a value > 0. In particular, larger \n\ + values indicate the solution should be more heavily regularized. This can be \n\ + useful when the dimensionality of the data is larger than the number of \n\ + samples. \n\ + - A good discussion of CCA can be found in the paper \"Canonical Correlation \n\ + Analysis\" by David Weenink. In particular, this function is implemented \n\ + using equations 29 and 30 from his paper. We also use the idea of doing CCA \n\ + on a reduced rank approximation of L and R as suggested by Paramveer S. \n\ + Dhillon in his paper \"Two Step CCA: A new spectral method for estimating \n\ + vector models of words\". " + + ); +} + + + diff --git a/ml/dlib/tools/python/src/cnn_face_detector.cpp b/ml/dlib/tools/python/src/cnn_face_detector.cpp new file mode 100644 index 000000000..f18d99d95 --- /dev/null +++ b/ml/dlib/tools/python/src/cnn_face_detector.cpp @@ -0,0 +1,183 @@ +// Copyright (C) 2017 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include +#include +#include +#include "indexing.h" +#include + +using namespace dlib; +using namespace std; + +namespace py = pybind11; + + +class cnn_face_detection_model_v1 +{ + +public: + + cnn_face_detection_model_v1(const std::string& model_filename) + { + deserialize(model_filename) >> net; + } + + std::vector detect ( + py::object pyimage, + const int upsample_num_times + ) + { + pyramid_down<2> pyr; + std::vector rects; + + // Copy the data into dlib based objects + matrix image; + if (is_gray_python_image(pyimage)) + assign_image(image, numpy_gray_image(pyimage)); + else if (is_rgb_python_image(pyimage)) + assign_image(image, numpy_rgb_image(pyimage)); + else + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); + + // 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_times; + while (levels > 0) + { + levels--; + pyramid_up(image, pyr); + } + + auto dets = net(image); + + // 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_times); + rects.push_back(d); + } + + return rects; + } + + std::vector > detect_mult ( + py::list imgs, + const int upsample_num_times, + const int batch_size = 128 + ) + { + pyramid_down<2> pyr; + std::vector > dimgs; + dimgs.reserve(len(imgs)); + + for(int i = 0; i < len(imgs); i++) + { + // Copy the data into dlib based objects + matrix image; + py::object tmp = imgs[i].cast(); + if (is_gray_python_image(tmp)) + assign_image(image, numpy_gray_image(tmp)); + else if (is_rgb_python_image(tmp)) + assign_image(image, numpy_rgb_image(tmp)); + else + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); + + for(int i = 0; i < upsample_num_times; i++) + { + pyramid_up(image); + } + dimgs.push_back(image); + } + + for(int i = 1; i < dimgs.size(); i++) + { + if + ( + dimgs[i - 1].nc() != dimgs[i].nc() || + dimgs[i - 1].nr() != dimgs[i].nr() + ) + throw dlib::error("Images in list must all have the same dimensions."); + + } + + auto dets = net(dimgs, batch_size); + std::vector > all_rects; + + for(auto&& im_dets : dets) + { + std::vector rects; + rects.reserve(im_dets.size()); + for (auto&& d : im_dets) { + d.rect = pyr.rect_down(d.rect, upsample_num_times); + rects.push_back(d); + } + all_rects.push_back(rects); + } + + return all_rects; + } + +private: + + template using con5d = con; + template using con5 = con; + + template using downsampler = relu>>>>>>>>; + template using rcon5 = relu>>; + + using net_type = loss_mmod>>>>>>>; + + net_type net; +}; + +// ---------------------------------------------------------------------------------------- + +void bind_cnn_face_detection(py::module& m) +{ + { + py::class_(m, "cnn_face_detection_model_v1", "This object detects human faces in an image. The constructor loads the face detection model from a file. You can download a pre-trained model from http://dlib.net/files/mmod_human_face_detector.dat.bz2.") + .def(py::init()) + .def( + "__call__", + &cnn_face_detection_model_v1::detect_mult, + py::arg("imgs"), py::arg("upsample_num_times")=0, py::arg("batch_size")=128, + "takes a list of images as input returning a 2d list of mmod rectangles" + ) + .def( + "__call__", + &cnn_face_detection_model_v1::detect, + py::arg("img"), py::arg("upsample_num_times")=0, + "Find faces in an image using a deep learning model.\n\ + - Upsamples the image upsample_num_times before running the face \n\ + detector." + ); + } + + m.def("set_dnn_prefer_smallest_algorithms", &set_dnn_prefer_smallest_algorithms, "Tells cuDNN to use slower algorithms that use less RAM."); + + auto cuda = m.def_submodule("cuda", "Routines for setting CUDA specific properties."); + cuda.def("set_device", &dlib::cuda::set_device, py::arg("device_id"), + "Set the active CUDA device. It is required that 0 <= device_id < get_num_devices()."); + cuda.def("get_device", &dlib::cuda::get_device, "Get the active CUDA device."); + cuda.def("get_num_devices", &dlib::cuda::get_num_devices, "Find out how many CUDA devices are available."); + + { + typedef mmod_rect type; + py::class_(m, "mmod_rectangle", "Wrapper around a rectangle object and a detection confidence score.") + .def_readwrite("rect", &type::rect) + .def_readwrite("confidence", &type::detection_confidence); + } + { + typedef std::vector type; + py::bind_vector(m, "mmod_rectangles", "An array of mmod rectangle objects.") + .def("extend", extend_vector_with_python_list); + } + { + typedef std::vector > type; + py::bind_vector(m, "mmod_rectangless", "A 2D array of mmod rectangle objects.") + .def("extend", extend_vector_with_python_list>); + } +} diff --git a/ml/dlib/tools/python/src/conversion.h b/ml/dlib/tools/python/src/conversion.h new file mode 100644 index 000000000..9ab2360a0 --- /dev/null +++ b/ml/dlib/tools/python/src/conversion.h @@ -0,0 +1,52 @@ +// Copyright (C) 2014 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_PYTHON_CONVERSION_H__ +#define DLIB_PYTHON_CONVERSION_H__ + +#include "opaque_types.h" +#include +#include + +using namespace dlib; +using namespace std; + +namespace py = pybind11; + +template +void pyimage_to_dlib_image(py::object img, dest_image_type& image) +{ + if (is_gray_python_image(img)) + assign_image(image, numpy_gray_image(img)); + else if (is_rgb_python_image(img)) + assign_image(image, numpy_rgb_image(img)); + else + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); +} + +template +void images_and_nested_params_to_dlib( + const py::object& pyimages, + const py::object& pyparams, + image_array& images, + std::vector >& params +) +{ + // Now copy the data into dlib based objects. + py::iterator image_it = pyimages.begin(); + py::iterator params_it = pyparams.begin(); + + for (unsigned long image_idx = 0; + image_it != pyimages.end() + && params_it != pyparams.end(); + ++image_it, ++params_it, ++image_idx) + { + for (py::iterator param_it = params_it->begin(); + param_it != params_it->end(); + ++param_it) + params[image_idx].push_back(param_it->cast()); + + pyimage_to_dlib_image(image_it->cast(), images[image_idx]); + } +} + +#endif // DLIB_PYTHON_CONVERSION_H__ diff --git a/ml/dlib/tools/python/src/correlation_tracker.cpp b/ml/dlib/tools/python/src/correlation_tracker.cpp new file mode 100644 index 000000000..1b17ba54c --- /dev/null +++ b/ml/dlib/tools/python/src/correlation_tracker.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2014 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include +#include + +using namespace dlib; +using namespace std; + +namespace py = pybind11; + +// ---------------------------------------------------------------------------------------- + +void start_track ( + correlation_tracker& tracker, + py::object img, + const drectangle& bounding_box +) +{ + if (is_gray_python_image(img)) + { + tracker.start_track(numpy_gray_image(img), bounding_box); + } + else if (is_rgb_python_image(img)) + { + tracker.start_track(numpy_rgb_image(img), bounding_box); + } + else + { + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); + } +} + +void start_track_rec ( + correlation_tracker& tracker, + py::object img, + const rectangle& bounding_box +) +{ + drectangle dbounding_box(bounding_box); + start_track(tracker, img, dbounding_box); +} + +double update ( + correlation_tracker& tracker, + py::object img +) +{ + if (is_gray_python_image(img)) + { + return tracker.update(numpy_gray_image(img)); + } + else if (is_rgb_python_image(img)) + { + return tracker.update(numpy_rgb_image(img)); + } + else + { + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); + } +} + +double update_guess ( + correlation_tracker& tracker, + py::object img, + const drectangle& bounding_box +) +{ + if (is_gray_python_image(img)) + { + return tracker.update(numpy_gray_image(img), bounding_box); + } + else if (is_rgb_python_image(img)) + { + return tracker.update(numpy_rgb_image(img), bounding_box); + } + else + { + throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); + } +} + +double update_guess_rec ( + correlation_tracker& tracker, + py::object img, + const rectangle& bounding_box +) +{ + drectangle dbounding_box(bounding_box); + return update_guess(tracker, img, dbounding_box); +} + +drectangle get_position (const correlation_tracker& tracker) { return tracker.get_position(); } + +// ---------------------------------------------------------------------------------------- + +void bind_correlation_tracker(py::module &m) +{ + { + typedef correlation_tracker type; + py::class_(m, "correlation_tracker", "This is a tool for tracking moving objects in a video stream. You give it \n\ + the bounding box of an object in the first frame and it attempts to track the \n\ + object in the box from frame to frame. \n\ + This tool is an implementation of the method described in the following paper: \n\ + Danelljan, Martin, et al. 'Accurate scale estimation for robust visual \n\ + tracking.' Proceedings of the British Machine Vision Conference BMVC. 2014.") + .def(py::init()) + .def("start_track", &::start_track, py::arg("image"), py::arg("bounding_box"), "\ + requires \n\ + - image is a numpy ndarray containing either an 8bit grayscale or RGB image. \n\ + - bounding_box.is_empty() == false \n\ + ensures \n\ + - This object will start tracking the thing inside the bounding box in the \n\ + given image. That is, if you call update() with subsequent video frames \n\ + then it will try to keep track of the position of the object inside bounding_box. \n\ + - #get_position() == bounding_box") + .def("start_track", &::start_track_rec, py::arg("image"), py::arg("bounding_box"), "\ + requires \n\ + - image is a numpy ndarray containing either an 8bit grayscale or RGB image. \n\ + - bounding_box.is_empty() == false \n\ + ensures \n\ + - This object will start tracking the thing inside the bounding box in the \n\ + given image. That is, if you call update() with subsequent video frames \n\ + then it will try to keep track of the position of the object inside bounding_box. \n\ + - #get_position() == bounding_box") + .def("update", &::update, py::arg("image"), "\ + requires \n\ + - image is a numpy ndarray containing either an 8bit grayscale or RGB image. \n\ + - get_position().is_empty() == false \n\ + (i.e. you must have started tracking by calling start_track()) \n\ + ensures \n\ + - performs: return update(img, get_position())") + .def("update", &::update_guess, py::arg("image"), py::arg("guess"), "\ + requires \n\ + - image is a numpy ndarray containing either an 8bit grayscale or RGB image. \n\ + - get_position().is_empty() == false \n\ + (i.e. you must have started tracking by calling start_track()) \n\ + ensures \n\ + - When searching for the object in img, we search in the area around the \n\ + provided guess. \n\ + - #get_position() == the new predicted location of the object in img. This \n\ + location will be a copy of guess that has been translated and scaled \n\ + appropriately based on the content of img so that it, hopefully, bounds \n\ + the object in img. \n\ + - Returns the peak to side-lobe ratio. This is a number that measures how \n\ + confident the tracker is that the object is inside #get_position(). \n\ + Larger values indicate higher confidence.") + .def("update", &::update_guess_rec, py::arg("image"), py::arg("guess"), "\ + requires \n\ + - image is a numpy ndarray containing either an 8bit grayscale or RGB image. \n\ + - get_position().is_empty() == false \n\ + (i.e. you must have started tracking by calling start_track()) \n\ + ensures \n\ + - When searching for the object in img, we search in the area around the \n\ + provided guess. \n\ + - #get_position() == the new predicted location of the object in img. This \n\ + location will be a copy of guess that has been translated and scaled \n\ + appropriately based on the content of img so that it, hopefully, bounds \n\ + the object in img. \n\ + - Returns the peak to side-lobe ratio. This is a number that measures how \n\ + confident the tracker is that the object is inside #get_position(). \n\ + Larger values indicate higher confidence.") + .def("get_position", &::get_position, "returns the predicted position of the object under track."); + } +} diff --git a/ml/dlib/tools/python/src/decision_functions.cpp b/ml/dlib/tools/python/src/decision_functions.cpp new file mode 100644 index 000000000..a93fe49b9 --- /dev/null +++ b/ml/dlib/tools/python/src/decision_functions.cpp @@ -0,0 +1,263 @@ +// Copyright (C) 2013 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include "testing_results.h" +#include + +using namespace dlib; +using namespace std; + +namespace py = pybind11; + +typedef matrix sample_type; +typedef std::vector > sparse_vect; + +template +double predict ( + const decision_function& df, + const typename decision_function::kernel_type::sample_type& samp +) +{ + typedef typename decision_function::kernel_type::sample_type T; + if (df.basis_vectors.size() == 0) + { + return 0; + } + else if (is_matrix::value && df.basis_vectors(0).size() != samp.size()) + { + std::ostringstream sout; + sout << "Input vector should have " << df.basis_vectors(0).size() + << " dimensions, not " << samp.size() << "."; + PyErr_SetString( PyExc_ValueError, sout.str().c_str() ); + throw py::error_already_set(); + } + return df(samp); +} + +template +void add_df ( + py::module& m, + const std::string name +) +{ + typedef decision_function df_type; + py::class_(m, name.c_str()) + .def("__call__", &predict) + .def(py::pickle(&getstate, &setstate)); +} + +template +typename df_type::sample_type get_weights( + const df_type& df +) +{ + if (df.basis_vectors.size() == 0) + { + PyErr_SetString( PyExc_ValueError, "Decision function is empty." ); + throw py::error_already_set(); + } + df_type temp = simplify_linear_decision_function(df); + return temp.basis_vectors(0); +} + +template +typename df_type::scalar_type get_bias( + const df_type& df +) +{ + if (df.basis_vectors.size() == 0) + { + PyErr_SetString( PyExc_ValueError, "Decision function is empty." ); + throw py::error_already_set(); + } + return df.b; +} + +template +void set_bias( + df_type& df, + double b +) +{ + if (df.basis_vectors.size() == 0) + { + PyErr_SetString( PyExc_ValueError, "Decision function is empty." ); + throw py::error_already_set(); + } + df.b = b; +} + +template +void add_linear_df ( + py::module &m, + const std::string name +) +{ + typedef decision_function df_type; + py::class_(m, name.c_str()) + .def("__call__", predict) + .def_property_readonly("weights", &get_weights) + .def_property("bias", get_bias, set_bias) + .def(py::pickle(&getstate, &setstate)); +} + +// ---------------------------------------------------------------------------------------- + +std::string binary_test__str__(const binary_test& item) +{ + std::ostringstream sout; + sout << "class1_accuracy: "<< item.class1_accuracy << " class2_accuracy: "<< item.class2_accuracy; + return sout.str(); +} +std::string binary_test__repr__(const binary_test& item) { return "< " + binary_test__str__(item) + " >";} + +std::string regression_test__str__(const regression_test& item) +{ + std::ostringstream sout; + sout << "mean_squared_error: "<< item.mean_squared_error << " R_squared: "<< item.R_squared; + sout << " mean_average_error: "<< item.mean_average_error << " mean_error_stddev: "<< item.mean_error_stddev; + return sout.str(); +} +std::string regression_test__repr__(const regression_test& item) { return "< " + regression_test__str__(item) + " >";} + +std::string ranking_test__str__(const ranking_test& item) +{ + std::ostringstream sout; + sout << "ranking_accuracy: "<< item.ranking_accuracy << " mean_ap: "<< item.mean_ap; + return sout.str(); +} +std::string ranking_test__repr__(const ranking_test& item) { return "< " + ranking_test__str__(item) + " >";} + +// ---------------------------------------------------------------------------------------- + +template +binary_test _test_binary_decision_function ( + const decision_function& dec_funct, + const std::vector& x_test, + const std::vector& y_test +) { return binary_test(test_binary_decision_function(dec_funct, x_test, y_test)); } + +template +regression_test _test_regression_function ( + const decision_function& reg_funct, + const std::vector& x_test, + const std::vector& y_test +) { return regression_test(test_regression_function(reg_funct, x_test, y_test)); } + +template < typename K > +ranking_test _test_ranking_function1 ( + const decision_function& funct, + const std::vector >& samples +) { return ranking_test(test_ranking_function(funct, samples)); } + +template < typename K > +ranking_test _test_ranking_function2 ( + const decision_function& funct, + const ranking_pair& sample +) { return ranking_test(test_ranking_function(funct, sample)); } + + +void bind_decision_functions(py::module &m) +{ + add_linear_df >(m, "_decision_function_linear"); + add_linear_df >(m, "_decision_function_sparse_linear"); + + add_df >(m, "_decision_function_histogram_intersection"); + add_df >(m, "_decision_function_sparse_histogram_intersection"); + + add_df >(m, "_decision_function_polynomial"); + add_df >(m, "_decision_function_sparse_polynomial"); + + add_df >(m, "_decision_function_radial_basis"); + add_df >(m, "_decision_function_sparse_radial_basis"); + + add_df >(m, "_decision_function_sigmoid"); + add_df >(m, "_decision_function_sparse_sigmoid"); + + + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + m.def("test_binary_decision_function", _test_binary_decision_function >, + py::arg("function"), py::arg("samples"), py::arg("labels")); + + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + m.def("test_regression_function", _test_regression_function >, + py::arg("function"), py::arg("samples"), py::arg("targets")); + + m.def("test_ranking_function", _test_ranking_function1 >, + py::arg("function"), py::arg("samples")); + m.def("test_ranking_function", _test_ranking_function1 >, + py::arg("function"), py::arg("samples")); + m.def("test_ranking_function", _test_ranking_function2 >, + py::arg("function"), py::arg("sample")); + m.def("test_ranking_function", _test_ranking_function2 >, + py::arg("function"), py::arg("sample")); + + + py::class_(m, "_binary_test") + .def("__str__", binary_test__str__) + .def("__repr__", binary_test__repr__) + .def_readwrite("class1_accuracy", &binary_test::class1_accuracy, + "A value between 0 and 1, measures accuracy on the +1 class.") + .def_readwrite("class2_accuracy", &binary_test::class2_accuracy, + "A value between 0 and 1, measures accuracy on the -1 class."); + + py::class_(m, "_ranking_test") + .def("__str__", ranking_test__str__) + .def("__repr__", ranking_test__repr__) + .def_readwrite("ranking_accuracy", &ranking_test::ranking_accuracy, + "A value between 0 and 1, measures the fraction of times a relevant sample was ordered before a non-relevant sample.") + .def_readwrite("mean_ap", &ranking_test::mean_ap, + "A value between 0 and 1, measures the mean average precision of the ranking."); + + py::class_(m, "_regression_test") + .def("__str__", regression_test__str__) + .def("__repr__", regression_test__repr__) + .def_readwrite("mean_average_error", ®ression_test::mean_average_error, + "The mean average error of a regression function on a dataset.") + .def_readwrite("mean_error_stddev", ®ression_test::mean_error_stddev, + "The standard deviation of the absolute value of the error of a regression function on a dataset.") + .def_readwrite("mean_squared_error", ®ression_test::mean_squared_error, + "The mean squared error of a regression function on a dataset.") + .def_readwrite("R_squared", ®ression_test::R_squared, + "A value between 0 and 1, measures the squared correlation between the output of a \n" + "regression function and the target values."); +} + + + diff --git a/ml/dlib/tools/python/src/dlib.cpp b/ml/dlib/tools/python/src/dlib.cpp new file mode 100644 index 000000000..ac6fea0db --- /dev/null +++ b/ml/dlib/tools/python/src/dlib.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2015 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include +#include + +namespace py = pybind11; + +void bind_matrix(py::module& m); +void bind_vector(py::module& m); +void bind_svm_c_trainer(py::module& m); +void bind_decision_functions(py::module& m); +void bind_basic_types(py::module& m); +void bind_other(py::module& m); +void bind_svm_rank_trainer(py::module& m); +void bind_cca(py::module& m); +void bind_sequence_segmenter(py::module& m); +void bind_svm_struct(py::module& m); +void bind_image_classes(py::module& m); +void bind_rectangles(py::module& m); +void bind_object_detection(py::module& m); +void bind_shape_predictors(py::module& m); +void bind_correlation_tracker(py::module& m); +void bind_face_recognition(py::module& m); +void bind_cnn_face_detection(py::module& m); +void bind_global_optimization(py::module& m); +void bind_numpy_returns(py::module& m); +void bind_image_dataset_metadata(py::module& m); + +#ifndef DLIB_NO_GUI_SUPPORT +void bind_gui(py::module& m); +#endif + +PYBIND11_MODULE(dlib, m) +{ + warn_about_unavailable_but_used_cpu_instructions(); + + +#define DLIB_QUOTE_STRING(x) DLIB_QUOTE_STRING2(x) +#define DLIB_QUOTE_STRING2(x) #x + m.attr("__version__") = DLIB_QUOTE_STRING(DLIB_VERSION); + m.attr("__time_compiled__") = std::string(__DATE__) + " " + std::string(__TIME__); + +#ifdef DLIB_USE_CUDA + m.attr("DLIB_USE_CUDA") = true; +#else + m.attr("DLIB_USE_CUDA") = false; +#endif +#ifdef DLIB_USE_BLAS + m.attr("DLIB_USE_BLAS") = true; +#else + m.attr("DLIB_USE_BLAS") = false; +#endif +#ifdef DLIB_USE_LAPACK + m.attr("DLIB_USE_LAPACK") = true; +#else + m.attr("DLIB_USE_LAPACK") = false; +#endif +#ifdef DLIB_HAVE_AVX + m.attr("USE_AVX_INSTRUCTIONS") = true; +#else + m.attr("USE_AVX_INSTRUCTIONS") = false; +#endif +#ifdef DLIB_HAVE_NEON + m.attr("USE_NEON_INSTRUCTIONS") = true; +#else + m.attr("USE_NEON_INSTRUCTIONS") = false; +#endif + + + + // Note that the order here matters. We need to do the basic types first. If we don't + // then what happens is the documentation created by sphinx will use horrible big + // template names to refer to C++ objects rather than the python names python users + // will expect. For instance, if bind_basic_types() isn't called early then when + // routines take a std::vector, rather than saying dlib.array in the python + // docs it will say "std::vector >" which is awful and + // confusing to python users. + // + // So when adding new things always add them to the end of the list. + bind_matrix(m); + bind_vector(m); + bind_basic_types(m); + bind_other(m); + + bind_svm_rank_trainer(m); + bind_decision_functions(m); + bind_cca(m); + bind_sequence_segmenter(m); + bind_svm_struct(m); + bind_image_classes(m); + bind_rectangles(m); + bind_object_detection(m); + bind_shape_predictors(m); + bind_correlation_tracker(m); + bind_face_recognition(m); + bind_cnn_face_detection(m); + bind_global_optimization(m); + bind_numpy_returns(m); + bind_svm_c_trainer(m); +#ifndef DLIB_NO_GUI_SUPPORT + bind_gui(m); +#endif + + bind_image_dataset_metadata(m); + + +} diff --git a/ml/dlib/tools/python/src/face_recognition.cpp b/ml/dlib/tools/python/src/face_recognition.cpp new file mode 100644 index 000000000..8d5dee678 --- /dev/null +++ b/ml/dlib/tools/python/src/face_recognition.cpp @@ -0,0 +1,245 @@ +// Copyright (C) 2017 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +#include "opaque_types.h" +#include +#include +#include +#include +#include +#include "indexing.h" +#include +#include +#include + + +using namespace dlib; +using namespace std; + +namespace py = pybind11; + + +typedef matrix cv; + +class face_recognition_model_v1 +{ + +public: + + face_recognition_model_v1(const std::string& model_filename) + { + deserialize(model_filename) >> net; + } + + matrix compute_face_descriptor ( + py::object img, + const full_object_detection& face, + const int num_jitters + ) + { + std::vector faces(1, face); + return compute_face_descriptors(img, faces, num_jitters)[0]; + } + + std::vector> compute_face_descriptors ( + py::object img, + const std::vector& faces, + const int num_jitters + ) + { + if (!is_rgb_python_image(img)) + throw dlib::error("Unsupported image type, must be RGB image."); + + for (auto& f : faces) + { + if (f.num_parts() != 68 && f.num_parts() != 5) + throw dlib::error("The full_object_detection must use the iBUG 300W 68 point face landmark style or dlib's 5 point style."); + } + + + std::vector dets; + for (auto& f : faces) + dets.push_back(get_face_chip_details(f, 150, 0.25)); + dlib::array> face_chips; + extract_image_chips(numpy_rgb_image(img), dets, face_chips); + + std::vector> face_descriptors; + face_descriptors.reserve(face_chips.size()); + + if (num_jitters <= 1) + { + // extract descriptors and convert from float vectors to double vectors + for (auto& d : net(face_chips,16)) + face_descriptors.push_back(matrix_cast(d)); + } + else + { + for (auto& fimg : face_chips) + face_descriptors.push_back(matrix_cast(mean(mat(net(jitter_image(fimg,num_jitters),16))))); + } + + return face_descriptors; + } + +private: + + dlib::rand rnd; + + std::vector> jitter_image( + const matrix& img, + const int num_jitters + ) + { + std::vector> crops; + for (int i = 0; i < num_jitters; ++i) + crops.push_back(dlib::jitter_image(img,rnd)); + return crops; + } + + + template