Commit 077a3b60 authored by Mischan Toosarani-Hausberger's avatar Mischan Toosarani-Hausberger Committed by Davis E. King
Browse files

Replace boost::python with pybind11 (#1040)

* Replace boost::python with pybind11

* Replace add_python_module with pybind11_add_module

* Fix clang error on type-dependent expression
parent c68bb4e7
......@@ -3,14 +3,15 @@
#ifndef DLIB_PYaSSERT_Hh_
#define DLIB_PYaSSERT_Hh_
#include <boost/python.hpp>
#include <pybind11/pybind11.h>
#define pyassert(_exp,_message) \
{if ( !(_exp) ) \
{ \
namespace py = pybind11; \
PyErr_SetString( PyExc_ValueError, _message ); \
boost::python::throw_error_already_set(); \
}}
throw py::error_already_set(); \
}}
#endif // DLIB_PYaSSERT_Hh_
// Copyright (C) 2013 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_BOOST_PYTHON_UtILS_Hh_
#define DLIB_BOOST_PYTHON_UtILS_Hh_
#ifndef DLIB_PYBIND_UtILS_Hh_
#define DLIB_PYBIND_UtILS_Hh_
#include <boost/python.hpp>
#include <pybind11/pybind11.h>
#include <vector>
#include <string>
#include <dlib/serialize.h>
inline bool hasattr(
boost::python::object obj,
const std::string& attr_name
)
/*!
ensures
- if (obj has an attribute named attr_name) then
- returns true
- else
- returns false
!*/
{
return PyObject_HasAttrString(obj.ptr(), attr_name.c_str());
}
// ----------------------------------------------------------------------------------------
namespace py = pybind11;
template <typename T>
std::vector<T> python_list_to_vector (
const boost::python::object& obj
const py::list& obj
)
/*!
ensures
......@@ -37,13 +22,13 @@ std::vector<T> python_list_to_vector (
std::vector<T> vect(len(obj));
for (unsigned long i = 0; i < vect.size(); ++i)
{
vect[i] = boost::python::extract<T>(obj[i]);
vect[i] = obj[i].cast<T>();
}
return vect;
}
template <typename T>
boost::python::list vector_to_python_list (
py::list vector_to_python_list (
const std::vector<T>& vect
)
/*!
......@@ -51,16 +36,30 @@ boost::python::list vector_to_python_list (
- converts a std::vector<T> into a python list object.
!*/
{
boost::python::list obj;
py::list obj;
for (unsigned long i = 0; i < vect.size(); ++i)
obj.append(vect[i]);
return obj;
}
template <typename T>
void extend_vector_with_python_list (
std::vector<T> &v,
const py::list &l
)
/*!
ensures
- appends items from a python list to the end of std::vector<T>.
!*/
{
for (const auto &item : l)
v.push_back(item.cast<T>());
}
// ----------------------------------------------------------------------------------------
template <typename T>
boost::shared_ptr<T> load_object_from_file (
std::shared_ptr<T> load_object_from_file (
const std::string& filename
)
/*!
......@@ -71,7 +70,7 @@ boost::shared_ptr<T> load_object_from_file (
std::ifstream fin(filename.c_str(), std::ios::binary);
if (!fin)
throw dlib::error("Unable to open " + filename);
boost::shared_ptr<T> obj(new T());
auto obj = std::make_shared<T>();
deserialize(*obj, fin);
return obj;
}
......@@ -79,5 +78,5 @@ boost::shared_ptr<T> load_object_from_file (
// ----------------------------------------------------------------------------------------
#endif // DLIB_BOOST_PYTHON_UtILS_Hh_
#endif // DLIB_PYBIND_UtILS_Hh_
......@@ -4,68 +4,63 @@
#define DLIB_SERIALIZE_PiCKLE_Hh_
#include <dlib/serialize.h>
#include <boost/python.hpp>
#include <pybind11/pybind11.h>
#include <sstream>
#include <dlib/vectorstream.h>
template <typename T>
struct serialize_pickle : boost::python::pickle_suite
template<typename T>
py::tuple getstate(const T& item)
{
static boost::python::tuple getstate(
const T& item
)
using namespace dlib;
std::vector<char> buf;
buf.reserve(5000);
vectorstream sout(buf);
serialize(item, sout);
return py::make_tuple(py::handle(
PyBytes_FromStringAndSize(buf.size()?&buf[0]:0, buf.size())));
}
template<typename T>
T setstate(py::tuple state)
{
using namespace dlib;
if (len(state) != 1)
{
using namespace dlib;
std::vector<char> buf;
buf.reserve(5000);
vectorstream sout(buf);
serialize(item, sout);
return boost::python::make_tuple(boost::python::handle<>(
PyBytes_FromStringAndSize(buf.size()?&buf[0]:0, buf.size())));
PyErr_SetObject(PyExc_ValueError,
py::str("expected 1-item tuple in call to __setstate__; got {}").format(state).ptr()
);
throw py::error_already_set();
}
static void setstate(
T& item,
boost::python::tuple state
)
// We used to serialize by converting to a str but the boost.python routines for
// doing this don't work in Python 3. You end up getting an error about invalid
// UTF-8 encodings. So instead we access the python C interface directly and use
// bytes objects. However, we keep the deserialization code that worked with str
// for backwards compatibility with previously pickled files.
T item;
py::object obj = state[0];
if (py::isinstance<py::str>(obj))
{
using namespace dlib;
using namespace boost::python;
if (len(state) != 1)
{
PyErr_SetObject(PyExc_ValueError,
("expected 1-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
// We used to serialize by converting to a str but the boost.python routines for
// doing this don't work in Python 3. You end up getting an error about invalid
// UTF-8 encodings. So instead we access the python C interface directly and use
// bytes objects. However, we keep the deserialization code that worked with str
// for backwards compatibility with previously pickled files.
if (boost::python::extract<str>(state[0]).check())
{
str data = boost::python::extract<str>(state[0]);
std::string temp(boost::python::extract<const char*>(data), len(data));
std::istringstream sin(temp);
deserialize(item, sin);
}
else if(PyBytes_Check(object(state[0]).ptr()))
{
object obj = state[0];
char* data = PyBytes_AsString(obj.ptr());
unsigned long num = PyBytes_Size(obj.ptr());
std::istringstream sin(std::string(data, num));
deserialize(item, sin);
}
else
{
throw error("Unable to unpickle, error in input file.");
}
py::str data = state[0].cast<py::str>();
std::string temp = data;
std::istringstream sin(temp);
deserialize(item, sin);
}
else if(PyBytes_Check(py::object(state[0]).ptr()))
{
py::object obj = state[0];
char* data = PyBytes_AsString(obj.ptr());
unsigned long num = PyBytes_Size(obj.ptr());
std::istringstream sin(std::string(data, num));
deserialize(item, sin);
}
else
{
throw error("Unable to unpickle, error in input file.");
}
};
return item;
}
#endif // DLIB_SERIALIZE_PiCKLE_Hh_
......@@ -26,8 +26,10 @@
#
import dlib
import pickle
try:
import cPickle as pickle
except ImportError:
import pickle
x = dlib.vectors()
y = dlib.array()
......@@ -62,5 +64,5 @@ print("prediction for second sample: {}".format(classifier(x[1])))
# classifier models can also be pickled in the same was as any other python object.
with open('saved_model.pickle', 'wb') as handle:
pickle.dump(classifier, handle)
pickle.dump(classifier, handle, 2)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
set(USE_SSE4_INSTRUCTIONS ON CACHE BOOL "Use SSE4 instructions")
# Make DLIB_ASSERT statements not abort the python interpreter, but just return an error.
add_definitions(-DDLIB_NO_ABORT_ON_2ND_FATAL_ERROR)
include(../../dlib/cmake_utils/add_python_module)
option(PYTHON3 "Build a Python3 compatible library rather than Python2." OFF)
# Avoid cmake warnings about changes in behavior of some Mac OS X path
# variable we don't care about.
if (POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
# Sometimes a computer will have multiple python verions installed. So in this
# block of code we find the one in the user's path and add its home folder into
# cmake's search path. That way it will use that version of python first.
if (PYTHON3)
find_program(PYTHON_EXECUTABLE python3)
endif()
if (NOT PYTHON_EXECUTABLE)
find_program(PYTHON_EXECUTABLE python)
endif()
# Resolve symbolic links, hopefully this will give us a path in the proper
# python home directory.
get_filename_component(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} REALPATH)
# Pick out the parent directories
get_filename_component(PYTHON_PATH ${PYTHON_EXECUTABLE} PATH)
get_filename_component(PYTHON_PATH ${PYTHON_PATH} PATH)
list(APPEND CMAKE_PREFIX_PATH "${PYTHON_PATH}")
if (CMAKE_COMPILER_IS_GNUCXX)
# Just setting CMAKE_POSITION_INDEPENDENT_CODE should be enough to set
# -fPIC for GCC but sometimes it still doesn't get set, so make sure it
# does.
add_definitions("-fPIC")
set(CMAKE_POSITION_INDEPENDENT_CODE True)
else()
set(CMAKE_POSITION_INDEPENDENT_CODE True)
endif()
# To avoid dll hell, always link everything statically when compiling in
# visual studio. This way, the resulting library won't depend on a bunch
# of other dll files and can be safely copied to someone elese's computer
# and expected to run.
if (MSVC)
include(${CMAKE_CURRENT_LIST_DIR}/../../dlib/cmake_utils/tell_visual_studio_to_use_static_runtime.cmake)
endif()
add_subdirectory(../../dlib/external/pybind11 ./pybind11_build)
# include dlib so we can link against it
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../dlib ./dlib_build)
if (USING_OLD_VISUAL_STUDIO_COMPILER)
message(FATAL_ERROR "You have to use a version of Visual Studio that supports C++11. As of December 2017, the only versions that have good enough C++11 support to compile the dlib Pyhton API is a fully updated Visual Studio 2015 or a fully updated Visual Studio 2017. Older versions of either of these compilers have bad C++11 support and will fail to compile the Python extension. ***SO UPDATE YOUR VISUAL STUDIO TO MAKE THIS ERROR GO AWAY***")
endif()
if (WIN32)
message(STATUS "USING PYTHON_LIBS: ${PYTHON_LIBRARIES}")
endif()
# Test for numpy
find_package(PythonInterp)
......@@ -62,8 +115,12 @@ if(NOT ${DLIB_NO_GUI_SUPPORT})
list(APPEND python_srcs src/gui.cpp)
endif()
add_python_module(dlib ${python_srcs})
pybind11_add_module(dlib_ ${python_srcs})
set_target_properties(dlib_
PROPERTIES OUTPUT_NAME dlib)
target_link_libraries(dlib_ PRIVATE dlib)
# When you run "make install" we will copy the compiled dlib.so (or dlib.pyd)
# library file to the python_examples folder.
install_dlib_to(../../python_examples)
# library file to the python_examples and the test folder.
install(TARGETS dlib_ LIBRARY DESTINATION ${CMAKE_CURRENT_LIST_DIR}/../../python_examples)
install(TARGETS dlib_ LIBRARY DESTINATION ${CMAKE_CURRENT_LIST_DIR}/test)
......@@ -4,34 +4,43 @@
#include <dlib/matrix.h>
#include <sstream>
#include <string>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <boost/python/suite/indexing/indexing_suite.hpp>
#include <boost/shared_ptr.hpp>
#include <dlib/string.h>
#include <pybind11/stl_bind.h>
using namespace std;
using namespace dlib;
using namespace boost::python;
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<double>);
boost::shared_ptr<std::vector<double> > array_from_object(object obj)
typedef std::vector<matrix<double,0,1>> column_vectors;
PYBIND11_MAKE_OPAQUE(column_vectors);
PYBIND11_MAKE_OPAQUE(std::vector<column_vectors>);
typedef pair<unsigned long,unsigned long> ulong_pair;
PYBIND11_MAKE_OPAQUE(ulong_pair);
PYBIND11_MAKE_OPAQUE(std::vector<ulong_pair>);
PYBIND11_MAKE_OPAQUE(std::vector<std::vector<ulong_pair>>);
typedef pair<unsigned long,double> ulong_double_pair;
PYBIND11_MAKE_OPAQUE(ulong_double_pair);
PYBIND11_MAKE_OPAQUE(std::vector<ulong_double_pair>);
PYBIND11_MAKE_OPAQUE(std::vector<std::vector<ulong_double_pair>>);
PYBIND11_MAKE_OPAQUE(std::vector<std::vector<std::vector<ulong_double_pair> > >);
std::shared_ptr<std::vector<double> > array_from_object(py::object obj)
{
extract<long> thesize(obj);
if (thesize.check())
{
long nr = thesize;
boost::shared_ptr<std::vector<double> > temp(new std::vector<double>(nr));
return temp;
}
else
{
const long nr = len(obj);
boost::shared_ptr<std::vector<double> > temp(new std::vector<double>(nr));
try {
long nr = obj.cast<long>();
return std::make_shared<std::vector<double>>(nr);
} catch (py::cast_error &e) {
py::list li = obj.cast<py::list>();
const long nr = len(li);
auto temp = std::make_shared<std::vector<double>>(nr);
for ( long r = 0; r < nr; ++r)
{
(*temp)[r] = extract<double>(obj[r]);
(*temp)[r] = li[r].cast<double>();
}
return temp;
}
......@@ -91,8 +100,7 @@ struct range_iter
else
{
PyErr_SetString(PyExc_StopIteration, "No more data.");
boost::python::throw_error_already_set();
return 0;
throw py::error_already_set();
}
}
};
......@@ -149,78 +157,91 @@ unsigned long range_len(const std::pair<unsigned long, unsigned long>& r)
template <typename T>
void resize(T& v, unsigned long n) { v.resize(n); }
void bind_basic_types()
void bind_basic_types(py::module& m)
{
class_<std::vector<double> >("array", "This object represents a 1D array of floating point numbers. "
"Moreover, it binds directly to the C++ type std::vector<double>.", init<>()
)
.def(vector_indexing_suite<std::vector<double> >())
.def("__init__", make_constructor(&array_from_object))
{
typedef double item_type;
typedef std::vector<item_type> type;
typedef std::shared_ptr<type> type_ptr;
py::bind_vector<type, type_ptr >(m, "array", "This object represents a 1D array of floating point numbers. "
"Moreover, it binds directly to the C++ type std::vector<double>.")
.def(py::init(&array_from_object))
.def("__str__", array__str__)
.def("__repr__", array__repr__)
.def("clear", &std::vector<double>::clear)
.def("resize", resize<std::vector<double> >)
.def_pickle(serialize_pickle<std::vector<double> >());
.def("clear", &type::clear)
.def("resize", resize<type>)
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
class_<std::vector<matrix<double,0,1> > >("vectors", "This object is an array of vector objects.")
.def(vector_indexing_suite<std::vector<matrix<double,0,1> > >())
.def("clear", &std::vector<matrix<double,0,1> >::clear)
.def("resize", resize<std::vector<matrix<double,0,1> > >)
.def_pickle(serialize_pickle<std::vector<matrix<double,0,1> > >());
{
typedef matrix<double,0,1> item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "vectors", "This object is an array of vector objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
{
typedef std::vector<std::vector<matrix<double,0,1> > > type;
class_<type>("vectorss", "This object is an array of arrays of vector objects.")
.def(vector_indexing_suite<type>())
typedef std::vector<matrix<double,0,1> > item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "vectorss", "This object is an array of arrays of vector objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def_pickle(serialize_pickle<type>());
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
typedef pair<unsigned long,unsigned long> range_type;
class_<range_type>("range", "This object is used to represent a range of elements in an array.", init<>() )
.def(init<unsigned long,unsigned long>())
py::class_<range_type>(m, "range", "This object is used to represent a range of elements in an array.")
.def(py::init<unsigned long,unsigned long>())
.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_pickle(serialize_pickle<range_type>());
.def(py::pickle(&getstate<range_type>, &setstate<range_type>));
class_<range_iter>("_range_iter")
py::class_<range_iter>(m, "_range_iter")
.def("next", &range_iter::next)
.def("__next__", &range_iter::next);
{
typedef std::vector<std::pair<unsigned long, unsigned long> > type;
class_<type>("ranges", "This object is an array of range objects.")
.def(vector_indexing_suite<type>())
typedef std::pair<unsigned long, unsigned long> item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "ranges", "This object is an array of range objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def_pickle(serialize_pickle<type>());
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
{
typedef std::vector<std::vector<std::pair<unsigned long, unsigned long> > > type;
class_<type>("rangess", "This object is an array of arrays of range objects.")
.def(vector_indexing_suite<type>())
typedef std::vector<std::pair<unsigned long, unsigned long> > item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "rangess", "This object is an array of arrays of range objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def_pickle(serialize_pickle<type>());
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
typedef pair<unsigned long,double> pair_type;
class_<pair_type>("pair", "This object is used to represent the elements of a sparse_vector.", init<>() )
.def(init<unsigned long,double>())
py::class_<pair_type>(m, "pair", "This object is used to represent the elements of a sparse_vector.")
.def(py::init<unsigned long,double>())
.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_pickle(serialize_pickle<pair_type>());
.def(py::pickle(&getstate<pair_type>, &setstate<pair_type>));
class_<std::vector<pair_type> >("sparse_vector",
{
typedef std::vector<pair_type> type;
py::bind_vector<type>(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\
......@@ -233,29 +254,34 @@ 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. "
dlib.make_sparse_vector() on it. "
)
.def(vector_indexing_suite<std::vector<pair_type> >())
.def("__str__", sparse_vector__str__)
.def("__repr__", sparse_vector__repr__)
.def("clear", &std::vector<pair_type >::clear)
.def("resize", resize<std::vector<pair_type > >)
.def_pickle(serialize_pickle<std::vector<pair_type> >());
class_<std::vector<std::vector<pair_type> > >("sparse_vectors", "This object is an array of sparse_vector objects.")
.def(vector_indexing_suite<std::vector<std::vector<pair_type> > >())
.def("clear", &std::vector<std::vector<pair_type> >::clear)
.def("resize", resize<std::vector<std::vector<pair_type> > >)
.def_pickle(serialize_pickle<std::vector<std::vector<pair_type> > >());
.def("clear", &type::clear)
.def("resize", resize<type>)
.def("extend", extend_vector_with_python_list<pair_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
{
typedef std::vector<std::vector<std::vector<pair_type> > > type;
class_<type>("sparse_vectorss", "This object is an array of arrays of sparse_vector objects.")
.def(vector_indexing_suite<type>())
typedef std::vector<pair_type> item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "sparse_vectors", "This object is an array of sparse_vector objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def_pickle(serialize_pickle<type>());
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
{
typedef std::vector<std::vector<pair_type> > item_type;
typedef std::vector<item_type > type;
py::bind_vector<type>(m, "sparse_vectorss", "This object is an array of arrays of sparse_vector objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def("extend", extend_vector_with_python_list<item_type>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
}
......@@ -2,12 +2,10 @@
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/python.h>
#include <boost/shared_ptr.hpp>
#include <dlib/statistics.h>
#include <boost/python/args.hpp>
using namespace dlib;
using namespace boost::python;
namespace py = pybind11;
typedef std::vector<std::pair<unsigned long,double> > sparse_vect;
......@@ -53,22 +51,22 @@ matrix<double,0,1> apply_cca_transform (
return sparse_matrix_vector_multiply(trans(m), v);
}
void bind_cca()
void bind_cca(py::module& m)
{
class_<cca_outputs>("cca_outputs")
.add_property("correlations", &cca_outputs::correlations)
.add_property("Ltrans", &cca_outputs::Ltrans)
.add_property("Rtrans", &cca_outputs::Rtrans);
py::class_<cca_outputs>(m, "cca_outputs")
.def_readwrite("correlations", &cca_outputs::correlations)
.def_readwrite("Ltrans", &cca_outputs::Ltrans)
.def_readwrite("Rtrans", &cca_outputs::Rtrans);
def("max_index_plus_one", sparse_vector_max_index_plus_one, arg("v"),
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. "
is empty then returns 0. "
);
def("apply_cca_transform", apply_cca_transform, (arg("m"), arg("v")),
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\
......@@ -77,7 +75,7 @@ ensures \n\
);
def("cca", _cca1, (arg("L"), arg("R"), arg("num_correlations"), arg("extra_rank")=5, arg("q")=2, arg("regularization")=0),
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\
......
......@@ -6,10 +6,15 @@
#include <dlib/dnn.h>
#include <dlib/image_transforms.h>
#include "indexing.h"
#include <pybind11/stl_bind.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<mmod_rect>);
PYBIND11_MAKE_OPAQUE(std::vector<std::vector<mmod_rect> >);
class cnn_face_detection_model_v1
{
......@@ -22,7 +27,7 @@ public:
}
std::vector<mmod_rect> detect (
object pyimage,
py::object pyimage,
const int upsample_num_times
)
{
......@@ -60,7 +65,7 @@ public:
}
std::vector<std::vector<mmod_rect> > detect_mult (
boost::python::list& imgs,
py::list& imgs,
const int upsample_num_times,
const int batch_size = 128
)
......@@ -73,7 +78,7 @@ public:
{
// Copy the data into dlib based objects
matrix<rgb_pixel> image;
object tmp = boost::python::extract<object>(imgs[i]);
py::object tmp = imgs[i].cast<py::object>();
if (is_gray_python_image(tmp))
assign_image(image, numpy_gray_image(tmp));
else if (is_rgb_python_image(tmp))
......@@ -131,15 +136,15 @@ private:
// ----------------------------------------------------------------------------------------
void bind_cnn_face_detection()
void bind_cnn_face_detection(py::module& m)
{
using boost::python::arg;
{
class_<cnn_face_detection_model_v1>("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.", init<std::string>())
py::class_<cnn_face_detection_model_v1>(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<std::string>())
.def(
"__call__",
&cnn_face_detection_model_v1::detect,
(arg("img"), arg("upsample_num_times")=0),
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."
......@@ -147,24 +152,24 @@ void bind_cnn_face_detection()
.def(
"__call__",
&cnn_face_detection_model_v1::detect_mult,
(arg("imgs"), arg("upsample_num_times")=0, arg("batch_size")=128),
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"
);
}
{
typedef mmod_rect type;
class_<type>("mmod_rectangle", "Wrapper around a rectangle object and a detection confidence score.")
py::class_<type>(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<mmod_rect> type;
class_<type>("mmod_rectangles", "An array of mmod rectangle objects.")
.def(vector_indexing_suite<type>());
py::bind_vector<type>(m, "mmod_rectangles", "An array of mmod rectangle objects.")
.def("extend", extend_vector_with_python_list<mmod_rect>);
}
{
typedef std::vector<std::vector<mmod_rect> > type;
class_<type>("mmod_rectangless", "A 2D array of mmod rectangle objects.")
.def(vector_indexing_suite<type>());
py::bind_vector<type>(m, "mmod_rectangless", "A 2D array of mmod rectangle objects.")
.def("extend", extend_vector_with_python_list<std::vector<mmod_rect>>);
}
}
......@@ -8,10 +8,11 @@
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
template <typename dest_image_type>
void pyimage_to_dlib_image(object img, dest_image_type& image)
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));
......@@ -23,21 +24,27 @@ void pyimage_to_dlib_image(object img, dest_image_type& image)
template <typename image_array, typename param_type>
void images_and_nested_params_to_dlib(
const object& pyimages,
const object& pyparams,
const py::object& pyimages,
const py::object& pyparams,
image_array& images,
std::vector<std::vector<param_type> >& params
)
{
const unsigned long num_images = len(pyimages);
// Now copy the data into dlib based objects.
for (unsigned long i = 0; i < num_images; ++i)
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)
{
const unsigned long num_params = len(pyparams[i]);
for (unsigned long j = 0; j < num_params; ++j)
params[i].push_back(extract<param_type>(pyparams[i][j]));
for (py::iterator param_it = params_it->begin();
param_it != params_it->end();
++param_it)
params[image_idx].push_back(param_it->cast<param_type>());
pyimage_to_dlib_image(pyimages[i], images[i]);
pyimage_to_dlib_image(image_it->cast<py::object>(), images[image_idx]);
}
}
......
......@@ -3,18 +3,18 @@
#include <dlib/python.h>
#include <dlib/geometry.h>
#include <boost/python/args.hpp>
#include <dlib/image_processing.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
void start_track (
correlation_tracker& tracker,
object img,
py::object img,
const drectangle& bounding_box
)
{
......@@ -34,7 +34,7 @@ void start_track (
void start_track_rec (
correlation_tracker& tracker,
object img,
py::object img,
const rectangle& bounding_box
)
{
......@@ -44,7 +44,7 @@ void start_track_rec (
double update (
correlation_tracker& tracker,
object img
py::object img
)
{
if (is_gray_python_image(img))
......@@ -63,7 +63,7 @@ double update (
double update_guess (
correlation_tracker& tracker,
object img,
py::object img,
const drectangle& bounding_box
)
{
......@@ -83,7 +83,7 @@ double update_guess (
double update_guess_rec (
correlation_tracker& tracker,
object img,
py::object img,
const rectangle& bounding_box
)
{
......@@ -95,18 +95,18 @@ drectangle get_position (const correlation_tracker& tracker) { return tracker.ge
// ----------------------------------------------------------------------------------------
void bind_correlation_tracker()
void bind_correlation_tracker(py::module &m)
{
using boost::python::arg;
{
typedef correlation_tracker type;
class_<type>("correlation_tracker", "This is a tool for tracking moving objects in a video stream. You give it \n\
py::class_<type>(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("start_track", &::start_track, (arg("image"), arg("bounding_box")), "\
.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\
......@@ -115,7 +115,7 @@ void bind_correlation_tracker()
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, (arg("image"), arg("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\
......@@ -124,14 +124,14 @@ void bind_correlation_tracker()
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, arg("image"), "\
.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, (arg("image"), arg("guess")), "\
.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\
......@@ -146,7 +146,7 @@ void bind_correlation_tracker()
- 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, (arg("image"), arg("guess")), "\
.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\
......
......@@ -3,17 +3,15 @@
#include <dlib/python.h>
#include "testing_results.h"
#include <boost/shared_ptr.hpp>
#include <boost/python/args.hpp>
#include <dlib/svm.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
typedef matrix<double,0,1> sample_type;
typedef std::vector<std::pair<unsigned long,double> > sparse_vect;
namespace py = pybind11;
typedef matrix<double,0,1> sample_type;
typedef std::vector<std::pair<unsigned long,double> > sparse_vect;
template <typename decision_function>
double predict (
......@@ -31,21 +29,22 @@ double predict (
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() );
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_ValueError, sout.str().c_str() );
throw py::error_already_set();
}
return df(samp);
}
template <typename kernel_type>
void add_df (
py::module& m,
const std::string name
)
{
typedef decision_function<kernel_type> df_type;
class_<df_type>(name.c_str())
py::class_<df_type>(m, name.c_str())
.def("__call__", &predict<df_type>)
.def_pickle(serialize_pickle<df_type>());
.def(py::pickle(&getstate<df_type>, &setstate<df_type>));
}
template <typename df_type>
......@@ -55,8 +54,8 @@ typename df_type::sample_type get_weights(
{
if (df.basis_vectors.size() == 0)
{
PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
boost::python::throw_error_already_set();
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);
......@@ -69,8 +68,8 @@ typename df_type::scalar_type get_bias(
{
if (df.basis_vectors.size() == 0)
{
PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
throw py::error_already_set();
}
return df.b;
}
......@@ -83,23 +82,24 @@ void set_bias(
{
if (df.basis_vectors.size() == 0)
{
PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_ValueError, "Decision function is empty." );
throw py::error_already_set();
}
df.b = b;
}
template <typename kernel_type>
void add_linear_df (
py::module &m,
const std::string name
)
{
typedef decision_function<kernel_type> df_type;
class_<df_type>(name.c_str())
py::class_<df_type>(m, name.c_str())
.def("__call__", predict<df_type>)
.add_property("weights", &get_weights<df_type>)
.add_property("bias", get_bias<df_type>, set_bias<df_type>)
.def_pickle(serialize_pickle<df_type>());
.def_property_readonly("weights", &get_weights<df_type>)
.def_property("bias", get_bias<df_type>, set_bias<df_type>)
.def(py::pickle(&getstate<df_type>, &setstate<df_type>));
}
// ----------------------------------------------------------------------------------------
......@@ -158,103 +158,102 @@ ranking_test _test_ranking_function2 (
) { return ranking_test(test_ranking_function(funct, sample)); }
void bind_decision_functions()
void bind_decision_functions(py::module &m)
{
using boost::python::arg;
add_linear_df<linear_kernel<sample_type> >("_decision_function_linear");
add_linear_df<sparse_linear_kernel<sparse_vect> >("_decision_function_sparse_linear");
add_df<histogram_intersection_kernel<sample_type> >("_decision_function_histogram_intersection");
add_df<sparse_histogram_intersection_kernel<sparse_vect> >("_decision_function_sparse_histogram_intersection");
add_df<polynomial_kernel<sample_type> >("_decision_function_polynomial");
add_df<sparse_polynomial_kernel<sparse_vect> >("_decision_function_sparse_polynomial");
add_df<radial_basis_kernel<sample_type> >("_decision_function_radial_basis");
add_df<sparse_radial_basis_kernel<sparse_vect> >("_decision_function_sparse_radial_basis");
add_df<sigmoid_kernel<sample_type> >("_decision_function_sigmoid");
add_df<sparse_sigmoid_kernel<sparse_vect> >("_decision_function_sparse_sigmoid");
def("test_binary_decision_function", _test_binary_decision_function<linear_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sparse_linear_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<radial_basis_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sparse_radial_basis_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<polynomial_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sparse_polynomial_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<histogram_intersection_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sparse_histogram_intersection_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sigmoid_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_binary_decision_function", _test_binary_decision_function<sparse_sigmoid_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("labels")));
def("test_regression_function", _test_regression_function<linear_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sparse_linear_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<radial_basis_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sparse_radial_basis_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<histogram_intersection_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sparse_histogram_intersection_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sigmoid_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sparse_sigmoid_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<polynomial_kernel<sample_type> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_regression_function", _test_regression_function<sparse_polynomial_kernel<sparse_vect> >,
(arg("function"), arg("samples"), arg("targets")));
def("test_ranking_function", _test_ranking_function1<linear_kernel<sample_type> >,
(arg("function"), arg("samples")));
def("test_ranking_function", _test_ranking_function1<sparse_linear_kernel<sparse_vect> >,
(arg("function"), arg("samples")));
def("test_ranking_function", _test_ranking_function2<linear_kernel<sample_type> >,
(arg("function"), arg("sample")));
def("test_ranking_function", _test_ranking_function2<sparse_linear_kernel<sparse_vect> >,
(arg("function"), arg("sample")));
class_<binary_test>("_binary_test")
add_linear_df<linear_kernel<sample_type> >(m, "_decision_function_linear");
add_linear_df<sparse_linear_kernel<sparse_vect> >(m, "_decision_function_sparse_linear");
add_df<histogram_intersection_kernel<sample_type> >(m, "_decision_function_histogram_intersection");
add_df<sparse_histogram_intersection_kernel<sparse_vect> >(m, "_decision_function_sparse_histogram_intersection");
add_df<polynomial_kernel<sample_type> >(m, "_decision_function_polynomial");
add_df<sparse_polynomial_kernel<sparse_vect> >(m, "_decision_function_sparse_polynomial");
add_df<radial_basis_kernel<sample_type> >(m, "_decision_function_radial_basis");
add_df<sparse_radial_basis_kernel<sparse_vect> >(m, "_decision_function_sparse_radial_basis");
add_df<sigmoid_kernel<sample_type> >(m, "_decision_function_sigmoid");
add_df<sparse_sigmoid_kernel<sparse_vect> >(m, "_decision_function_sparse_sigmoid");
m.def("test_binary_decision_function", _test_binary_decision_function<linear_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sparse_linear_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<radial_basis_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sparse_radial_basis_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<polynomial_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sparse_polynomial_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<histogram_intersection_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sparse_histogram_intersection_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sigmoid_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_binary_decision_function", _test_binary_decision_function<sparse_sigmoid_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("labels"));
m.def("test_regression_function", _test_regression_function<linear_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sparse_linear_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<radial_basis_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sparse_radial_basis_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<histogram_intersection_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sparse_histogram_intersection_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sigmoid_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sparse_sigmoid_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<polynomial_kernel<sample_type> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_regression_function", _test_regression_function<sparse_polynomial_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"), py::arg("targets"));
m.def("test_ranking_function", _test_ranking_function1<linear_kernel<sample_type> >,
py::arg("function"), py::arg("samples"));
m.def("test_ranking_function", _test_ranking_function1<sparse_linear_kernel<sparse_vect> >,
py::arg("function"), py::arg("samples"));
m.def("test_ranking_function", _test_ranking_function2<linear_kernel<sample_type> >,
py::arg("function"), py::arg("sample"));
m.def("test_ranking_function", _test_ranking_function2<sparse_linear_kernel<sparse_vect> >,
py::arg("function"), py::arg("sample"));
py::class_<binary_test>(m, "_binary_test")
.def("__str__", binary_test__str__)
.def("__repr__", binary_test__repr__)
.add_property("class1_accuracy", &binary_test::class1_accuracy,
.def_readwrite("class1_accuracy", &binary_test::class1_accuracy,
"A value between 0 and 1, measures accuracy on the +1 class.")
.add_property("class2_accuracy", &binary_test::class2_accuracy,
.def_readwrite("class2_accuracy", &binary_test::class2_accuracy,
"A value between 0 and 1, measures accuracy on the -1 class.");
class_<ranking_test>("_ranking_test")
py::class_<ranking_test>(m, "_ranking_test")
.def("__str__", ranking_test__str__)
.def("__repr__", ranking_test__repr__)
.add_property("ranking_accuracy", &ranking_test::ranking_accuracy,
.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.")
.add_property("mean_ap", &ranking_test::mean_ap,
.def_readwrite("mean_ap", &ranking_test::mean_ap,
"A value between 0 and 1, measures the mean average precision of the ranking.");
class_<regression_test>("_regression_test")
py::class_<regression_test>(m, "_regression_test")
.def("__str__", regression_test__str__)
.def("__repr__", regression_test__repr__)
.add_property("mean_average_error", &regression_test::mean_average_error,
.def_readwrite("mean_average_error", &regression_test::mean_average_error,
"The mean average error of a regression function on a dataset.")
.add_property("mean_error_stddev", &regression_test::mean_error_stddev,
.def_readwrite("mean_error_stddev", &regression_test::mean_error_stddev,
"The standard deviation of the absolute value of the error of a regression function on a dataset.")
.add_property("mean_squared_error", &regression_test::mean_squared_error,
.def_readwrite("mean_squared_error", &regression_test::mean_squared_error,
"The mean squared error of a regression function on a dataset.")
.add_property("R_squared", &regression_test::R_squared,
.def_readwrite("R_squared", &regression_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.");
}
......
// Copyright (C) 2015 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include <boost/python.hpp>
#include <pybind11/pybind11.h>
void bind_matrix();
void bind_vector();
void bind_svm_c_trainer();
void bind_decision_functions();
void bind_basic_types();
void bind_other();
void bind_svm_rank_trainer();
void bind_cca();
void bind_sequence_segmenter();
void bind_svm_struct();
void bind_image_classes();
void bind_rectangles();
void bind_object_detection();
void bind_shape_predictors();
void bind_correlation_tracker();
void bind_face_recognition();
void bind_cnn_face_detection();
void bind_global_optimization();
void bind_numpy_returns();
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);
#ifndef DLIB_NO_GUI_SUPPORT
void bind_gui();
void bind_gui(py::module& m);
#endif
BOOST_PYTHON_MODULE(dlib)
PYBIND11_MODULE(dlib, m)
{
// Disable printing of the C++ function signature in the python __doc__ string
// since it is full of huge amounts of template clutter.
boost::python::docstring_options options(true,true,false);
py::options options;
options.disable_function_signatures();
#define DLIB_QUOTE_STRING(x) DLIB_QUOTE_STRING2(x)
#define DLIB_QUOTE_STRING2(x) #x
m.attr("__version__") = DLIB_QUOTE_STRING(DLIB_VERSION);
boost::python::scope().attr("__version__") = DLIB_QUOTE_STRING(DLIB_VERSION);
bind_matrix();
bind_vector();
bind_svm_c_trainer();
bind_decision_functions();
bind_basic_types();
bind_other();
bind_svm_rank_trainer();
bind_cca();
bind_sequence_segmenter();
bind_svm_struct();
bind_image_classes();
bind_rectangles();
bind_object_detection();
bind_shape_predictors();
bind_correlation_tracker();
bind_face_recognition();
bind_cnn_face_detection();
bind_global_optimization();
bind_numpy_returns();
bind_matrix(m);
bind_vector(m);
bind_svm_c_trainer(m);
bind_decision_functions(m);
bind_basic_types(m);
bind_other(m);
bind_svm_rank_trainer(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);
#ifndef DLIB_NO_GUI_SUPPORT
bind_gui();
bind_gui(m);
#endif
}
......@@ -2,23 +2,24 @@
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/python.h>
#include <boost/shared_ptr.hpp>
#include <dlib/matrix.h>
#include <boost/python/slice.hpp>
#include <dlib/geometry/vector.h>
#include <dlib/dnn.h>
#include <dlib/image_transforms.h>
#include "indexing.h"
#include <dlib/image_io.h>
#include <dlib/clustering.h>
#include <pybind11/stl_bind.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
typedef matrix<double,0,1> cv;
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<full_object_detection>);
typedef matrix<double,0,1> cv;
class face_recognition_model_v1
{
......@@ -31,7 +32,7 @@ public:
}
matrix<double,0,1> compute_face_descriptor (
object img,
py::object img,
const full_object_detection& face,
const int num_jitters
)
......@@ -41,7 +42,7 @@ public:
}
std::vector<matrix<double,0,1>> compute_face_descriptors (
object img,
py::object img,
const std::vector<full_object_detection>& faces,
const int num_jitters
)
......@@ -128,12 +129,12 @@ private:
// ----------------------------------------------------------------------------------------
boost::python::list chinese_whispers_clustering(boost::python::list descriptors, float threshold)
py::list chinese_whispers_clustering(py::list descriptors, float threshold)
{
DLIB_CASSERT(threshold > 0);
boost::python::list clusters;
py::list clusters;
size_t num_descriptors = len(descriptors);
size_t num_descriptors = py::len(descriptors);
// This next bit of code creates a graph of connected objects and then uses the Chinese
// whispers graph clustering algorithm to identify how many objects there are and which
......@@ -144,8 +145,8 @@ boost::python::list chinese_whispers_clustering(boost::python::list descriptors,
{
for (size_t j = i; j < num_descriptors; ++j)
{
matrix<double,0,1>& first_descriptor = boost::python::extract<matrix<double,0,1>&>(descriptors[i]);
matrix<double,0,1>& second_descriptor = boost::python::extract<matrix<double,0,1>&>(descriptors[j]);
matrix<double,0,1>& first_descriptor = descriptors[i].cast<matrix<double,0,1>&>();
matrix<double,0,1>& second_descriptor = descriptors[j].cast<matrix<double,0,1>&>();
if (length(first_descriptor-second_descriptor) < threshold)
edges.push_back(sample_pair(i,j));
......@@ -160,7 +161,7 @@ boost::python::list chinese_whispers_clustering(boost::python::list descriptors,
}
void save_face_chips (
object img,
py::object img,
const std::vector<full_object_detection>& faces,
const std::string& chip_filename,
size_t size = 150,
......@@ -194,7 +195,7 @@ void save_face_chips (
}
void save_face_chip (
object img,
py::object img,
const full_object_detection& face,
const std::string& chip_filename,
size_t size = 150,
......@@ -206,43 +207,39 @@ void save_face_chip (
return;
}
BOOST_PYTHON_FUNCTION_OVERLOADS(save_face_chip_with_defaults, save_face_chip, 3, 5)
BOOST_PYTHON_FUNCTION_OVERLOADS(save_face_chips_with_defaults, save_face_chips, 3, 5)
void bind_face_recognition()
void bind_face_recognition(py::module &m)
{
using boost::python::arg;
{
class_<face_recognition_model_v1>("face_recognition_model_v1", "This object maps human faces into 128D vectors where pictures of the same person are mapped near to each other and pictures of different people are mapped far apart. The constructor loads the face recognition model from a file. The model file is available here: http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2", init<std::string>())
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptor, (arg("img"),arg("face"),arg("num_jitters")=0),
py::class_<face_recognition_model_v1>(m, "face_recognition_model_v1", "This object maps human faces into 128D vectors where pictures of the same person are mapped near to each other and pictures of different people are mapped far apart. The constructor loads the face recognition model from a file. The model file is available here: http://dlib.net/files/dlib_face_recognition_resnet_model_v1.dat.bz2")
.def(py::init<std::string>())
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptor, py::arg("img"),py::arg("face"),py::arg("num_jitters")=0,
"Takes an image and a full_object_detection that references a face in that image and converts it into a 128D face descriptor. "
"If num_jitters>1 then each face will be randomly jittered slightly num_jitters times, each run through the 128D projection, and the average used as the face descriptor."
)
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptors, (arg("img"),arg("faces"),arg("num_jitters")=0),
.def("compute_face_descriptor", &face_recognition_model_v1::compute_face_descriptors, py::arg("img"),py::arg("faces"),py::arg("num_jitters")=0,
"Takes an image and an array of full_object_detections that reference faces in that image and converts them into 128D face descriptors. "
"If num_jitters>1 then each face will be randomly jittered slightly num_jitters times, each run through the 128D projection, and the average used as the face descriptor."
);
}
def("save_face_chip", &save_face_chip, save_face_chip_with_defaults(
m.def("save_face_chip", &save_face_chip,
"Takes an image and a full_object_detection that references a face in that image and saves the face with the specified file name prefix. The face will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("face"), arg("chip_filename"), arg("size"), arg("padding"))
));
def("save_face_chips", &save_face_chips, save_face_chips_with_defaults(
py::arg("img"), py::arg("face"), py::arg("chip_filename"), py::arg("size")=150, py::arg("padding")=0.25
);
m.def("save_face_chips", &save_face_chips,
"Takes an image and a full_object_detections object that reference faces in that image and saves the faces with the specified file name prefix. The faces will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("faces"), arg("chip_filename"), arg("size"), arg("padding"))
));
def("chinese_whispers_clustering", &chinese_whispers_clustering, (arg("descriptors"), arg("threshold")),
py::arg("img"), py::arg("faces"), py::arg("chip_filename"), py::arg("size")=150, py::arg("padding")=0.25
);
m.def("chinese_whispers_clustering", &chinese_whispers_clustering, py::arg("descriptors"), py::arg("threshold"),
"Takes a list of descriptors and returns a list that contains a label for each descriptor. Clustering is done using dlib::chinese_whispers."
);
{
{
typedef std::vector<full_object_detection> type;
class_<type>("full_object_detections", "An array of full_object_detection objects.")
.def(vector_indexing_suite<type>())
py::bind_vector<type>(m, "full_object_detections", "An array of full_object_detection objects.")
.def("clear", &type::clear)
.def("resize", resize<type>)
.def_pickle(serialize_pickle<type>());
.def("extend", extend_vector_with_python_list<full_object_detection>)
.def(py::pickle(&getstate<type>, &setstate<type>));
}
}
......@@ -2,67 +2,65 @@
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/python.h>
#include <boost/shared_ptr.hpp>
#include <dlib/global_optimization.h>
#include <dlib/matrix.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
std::vector<bool> list_to_bool_vector(
const boost::python::list& l
const py::list& l
)
{
std::vector<bool> result(len(l));
for (long i = 0; i < result.size(); ++i)
{
result[i] = extract<bool>(l[i]);
result[i] = l[i].cast<bool>();
cout << "bool val: " << result[i] << endl;
}
return result;
}
matrix<double,0,1> list_to_mat(
const boost::python::list& l
const py::list& l
)
{
matrix<double,0,1> result(len(l));
for (long i = 0; i < result.size(); ++i)
result(i) = extract<double>(l[i]);
result(i) = l[i].cast<double>();
return result;
}
boost::python::list mat_to_list (
py::list mat_to_list (
const matrix<double,0,1>& m
)
{
boost::python::list l;
py::list l;
for (long i = 0; i < m.size(); ++i)
l.append(m(i));
return l;
}
size_t num_function_arguments(object f)
size_t num_function_arguments(py::object f)
{
if (hasattr(f,"func_code"))
return boost::python::extract<std::size_t>(f.attr("func_code").attr("co_argcount"));
return f.attr("func_code").attr("co_argcount").cast<std::size_t>();
else
return boost::python::extract<std::size_t>(f.attr("__code__").attr("co_argcount"));
return f.attr("__code__").attr("co_argcount").cast<std::size_t>();
}
double call_func(object f, const matrix<double,0,1>& args)
double call_func(py::object f, const matrix<double,0,1>& args)
{
const auto num = num_function_arguments(f);
DLIB_CASSERT(num == args.size(),
"The function being optimized takes a number of arguments that doesn't agree with the size of the bounds lists you provided to find_max_global()");
DLIB_CASSERT(0 < num && num < 15, "Functions being optimized must take between 1 and 15 scalar arguments.");
#define CALL_WITH_N_ARGS(N) case N: return extract<double>(dlib::gopt_impl::_cwv(f,args,typename make_compile_time_integer_range<N>::type()));
#define CALL_WITH_N_ARGS(N) case N: return dlib::gopt_impl::_cwv(f,args,typename make_compile_time_integer_range<N>::type()).cast<double>();
switch (num)
{
CALL_WITH_N_ARGS(1)
......@@ -89,11 +87,11 @@ double call_func(object f, const matrix<double,0,1>& args)
// ----------------------------------------------------------------------------------------
boost::python::tuple py_find_max_global (
object f,
boost::python::list bound1,
boost::python::list bound2,
boost::python::list is_integer_variable,
py::tuple py_find_max_global (
py::object f,
py::list bound1,
py::list bound2,
py::list is_integer_variable,
unsigned long num_function_calls,
double solver_epsilon = 0
)
......@@ -110,13 +108,13 @@ boost::python::tuple py_find_max_global (
list_to_bool_vector(is_integer_variable), max_function_calls(num_function_calls),
solver_epsilon);
return boost::python::make_tuple(mat_to_list(result.x),result.y);
return py::make_tuple(mat_to_list(result.x),result.y);
}
boost::python::tuple py_find_max_global2 (
object f,
boost::python::list bound1,
boost::python::list bound2,
py::tuple py_find_max_global2 (
py::object f,
py::list bound1,
py::list bound2,
unsigned long num_function_calls,
double solver_epsilon = 0
)
......@@ -130,16 +128,16 @@ boost::python::tuple py_find_max_global2 (
auto result = find_max_global(func, list_to_mat(bound1), list_to_mat(bound2), max_function_calls(num_function_calls), solver_epsilon);
return boost::python::make_tuple(mat_to_list(result.x),result.y);
return py::make_tuple(mat_to_list(result.x),result.y);
}
// ----------------------------------------------------------------------------------------
boost::python::tuple py_find_min_global (
object f,
boost::python::list bound1,
boost::python::list bound2,
boost::python::list is_integer_variable,
py::tuple py_find_min_global (
py::object f,
py::list bound1,
py::list bound2,
py::list is_integer_variable,
unsigned long num_function_calls,
double solver_epsilon = 0
)
......@@ -156,13 +154,13 @@ boost::python::tuple py_find_min_global (
list_to_bool_vector(is_integer_variable), max_function_calls(num_function_calls),
solver_epsilon);
return boost::python::make_tuple(mat_to_list(result.x),result.y);
return py::make_tuple(mat_to_list(result.x),result.y);
}
boost::python::tuple py_find_min_global2 (
object f,
boost::python::list bound1,
boost::python::list bound2,
py::tuple py_find_min_global2 (
py::object f,
py::list bound1,
py::list bound2,
unsigned long num_function_calls,
double solver_epsilon = 0
)
......@@ -176,12 +174,12 @@ boost::python::tuple py_find_min_global2 (
auto result = find_min_global(func, list_to_mat(bound1), list_to_mat(bound2), max_function_calls(num_function_calls), solver_epsilon);
return boost::python::make_tuple(mat_to_list(result.x),result.y);
return py::make_tuple(mat_to_list(result.x),result.y);
}
// ----------------------------------------------------------------------------------------
void bind_global_optimization()
void bind_global_optimization(py::module& m)
{
/*!
requires
......@@ -232,9 +230,8 @@ void bind_global_optimization()
supplied functions. In most cases, it improves the efficiency of the
optimizer.
!*/
using boost::python::arg;
{
def("find_max_global", &py_find_max_global,
m.def("find_max_global", &py_find_max_global,
"requires \n\
- len(bound1) == len(bound2) == len(is_integer_variable) \n\
- for all valid i: bound1[i] != bound2[i] \n\
......@@ -283,31 +280,31 @@ ensures \n\
supplied functions. In most cases, it improves the efficiency of the \n\
optimizer."
,
(arg("f"), arg("bound1"), arg("bound2"), arg("is_integer_variable"), arg("num_function_calls"), arg("solver_epsilon")=0)
py::arg("f"), py::arg("bound1"), py::arg("bound2"), py::arg("is_integer_variable"), py::arg("num_function_calls"), py::arg("solver_epsilon")=0
);
}
{
def("find_max_global", &py_find_max_global2,
m.def("find_max_global", &py_find_max_global2,
"This function simply calls the other version of find_max_global() with is_integer_variable set to False for all variables.",
(arg("f"), arg("bound1"), arg("bound2"), arg("num_function_calls"), arg("solver_epsilon")=0)
py::arg("f"), py::arg("bound1"), py::arg("bound2"), py::arg("num_function_calls"), py::arg("solver_epsilon")=0
);
}
{
def("find_min_global", &py_find_min_global,
m.def("find_min_global", &py_find_min_global,
"This function is just like find_max_global(), except it performs minimization rather than maximization."
,
(arg("f"), arg("bound1"), arg("bound2"), arg("is_integer_variable"), arg("num_function_calls"), arg("solver_epsilon")=0)
py::arg("f"), py::arg("bound1"), py::arg("bound2"), py::arg("is_integer_variable"), py::arg("num_function_calls"), py::arg("solver_epsilon")=0
);
}
{
def("find_min_global", &py_find_min_global2,
m.def("find_min_global", &py_find_min_global2,
"This function simply calls the other version of find_min_global() with is_integer_variable set to False for all variables.",
(arg("f"), arg("bound1"), arg("bound2"), arg("num_function_calls"), arg("solver_epsilon")=0)
py::arg("f"), py::arg("bound1"), py::arg("bound2"), py::arg("num_function_calls"), py::arg("solver_epsilon")=0
);
}
......
#ifndef DLIB_NO_GUI_SUPPORT
#include <dlib/python.h>
#include <boost/python/args.hpp>
#include <dlib/geometry.h>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
......@@ -10,7 +9,8 @@
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
......@@ -34,7 +34,7 @@ void image_window_set_image_simple_detector_py (
void image_window_set_image (
image_window& win,
object img
py::object img
)
{
if (is_gray_python_image(img))
......@@ -73,16 +73,16 @@ void add_overlay_parts (
win.add_overlay(render_face_detections(detection, color));
}
boost::shared_ptr<image_window> make_image_window_from_image(object img)
std::shared_ptr<image_window> make_image_window_from_image(py::object img)
{
boost::shared_ptr<image_window> win(new image_window);
auto win = std::make_shared<image_window>();
image_window_set_image(*win, img);
return win;
}
boost::shared_ptr<image_window> make_image_window_from_image_and_title(object img, const string& title)
std::shared_ptr<image_window> make_image_window_from_image_and_title(py::object img, const string& title)
{
boost::shared_ptr<image_window> win(new image_window);
auto win = std::make_shared<image_window>();
image_window_set_image(*win, img);
win->set_title(title);
return win;
......@@ -90,35 +90,35 @@ boost::shared_ptr<image_window> make_image_window_from_image_and_title(object im
// ----------------------------------------------------------------------------------------
void bind_gui()
void bind_gui(py::module& m)
{
using boost::python::arg;
{
typedef image_window type;
typedef void (image_window::*set_title_funct)(const std::string&);
typedef void (image_window::*add_overlay_funct)(const std::vector<rectangle>& r, rgb_pixel p);
class_<type,boost::noncopyable>("image_window",
py::class_<type, std::shared_ptr<type>>(m, "image_window",
"This is a GUI window capable of showing images on the screen.")
.def("__init__", make_constructor(&make_image_window_from_image),
.def(py::init())
.def(py::init(&make_image_window_from_image),
"Create an image window that displays the given numpy image.")
.def("__init__", make_constructor(&make_image_window_from_image_and_title),
.def(py::init(&make_image_window_from_image_and_title),
"Create an image window that displays the given numpy image and also has the given title.")
.def("set_image", image_window_set_image, arg("image"),
"Make the image_window display the given image.")
.def("set_image", image_window_set_image_fhog_detector, arg("detector"),
.def("set_image", image_window_set_image_simple_detector_py, py::arg("detector"),
"Make the image_window display the given HOG detector's filters.")
.def("set_image", image_window_set_image_simple_detector_py, arg("detector"),
.def("set_image", image_window_set_image_fhog_detector, py::arg("detector"),
"Make the image_window display the given HOG detector's filters.")
.def("set_title", (set_title_funct)&type::set_title, arg("title"),
.def("set_image", image_window_set_image, py::arg("image"),
"Make the image_window display the given image.")
.def("set_title", (set_title_funct)&type::set_title, py::arg("title"),
"Set the title of the window to the given value.")
.def("clear_overlay", &type::clear_overlay, "Remove all overlays from the image_window.")
.def("add_overlay", (add_overlay_funct)&type::add_overlay<rgb_pixel>, (arg("rectangles"), arg("color")=rgb_pixel(255, 0, 0)),
.def("add_overlay", (add_overlay_funct)&type::add_overlay<rgb_pixel>, py::arg("rectangles"), py::arg("color")=rgb_pixel(255, 0, 0),
"Add a list of rectangles to the image_window. They will be displayed as red boxes by default, but the color can be passed.")
.def("add_overlay", add_overlay_rect, (arg("rectangle"), arg("color")=rgb_pixel(255, 0, 0)),
.def("add_overlay", add_overlay_rect, py::arg("rectangle"), py::arg("color")=rgb_pixel(255, 0, 0),
"Add a rectangle to the image_window. It will be displayed as a red box by default, but the color can be passed.")
.def("add_overlay", add_overlay_drect, (arg("rectangle"), arg("color")=rgb_pixel(255, 0, 0)),
.def("add_overlay", add_overlay_drect, py::arg("rectangle"), py::arg("color")=rgb_pixel(255, 0, 0),
"Add a rectangle to the image_window. It will be displayed as a red box by default, but the color can be passed.")
.def("add_overlay", add_overlay_parts, (arg("detection"), arg("color")=rgb_pixel(0, 0, 255)),
.def("add_overlay", add_overlay_parts, py::arg("detection"), py::arg("color")=rgb_pixel(0, 0, 255),
"Add full_object_detection parts to the image window. They will be displayed as blue lines by default, but the color can be passed.")
.def("wait_until_closed", &type::wait_until_closed,
"This function blocks until the window is closed.");
......
#include <dlib/python.h>
#include <boost/python/args.hpp>
#include "dlib/pixel.h"
#include <dlib/image_transforms.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
......@@ -21,20 +21,19 @@ string print_rgb_pixel_str(const rgb_pixel& p)
string print_rgb_pixel_repr(const rgb_pixel& p)
{
std::ostringstream sout;
sout << "rgb_pixel(" << p.red << "," << p.green << "," << p.blue << ")";
sout << "rgb_pixel(" << (int)p.red << "," << (int)p.green << "," << (int)p.blue << ")";
return sout.str();
}
// ----------------------------------------------------------------------------------------
void bind_image_classes()
void bind_image_classes(py::module& m)
{
using boost::python::arg;
class_<rgb_pixel>("rgb_pixel")
.def(init<unsigned char,unsigned char,unsigned char>( (arg("red"),arg("green"),arg("blue")) ))
py::class_<rgb_pixel>(m, "rgb_pixel")
.def(py::init<unsigned char,unsigned char,unsigned char>(), py::arg("red"), py::arg("green"), py::arg("blue"))
.def("__str__", &print_rgb_pixel_str)
.def("__repr__", &print_rgb_pixel_repr)
.add_property("red", &rgb_pixel::red)
.add_property("green", &rgb_pixel::green)
.add_property("blue", &rgb_pixel::blue);
.def_readwrite("red", &rgb_pixel::red)
.def_readwrite("green", &rgb_pixel::green)
.def_readwrite("blue", &rgb_pixel::blue);
}
......@@ -3,8 +3,6 @@
#ifndef DLIB_PYTHON_INDEXING_H__
#define DLIB_PYTHON_INDEXING_H__
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
namespace dlib
{
template <typename T>
......
......@@ -2,13 +2,12 @@
// License: Boost Software License See LICENSE.txt for the full license.
#include <dlib/python.h>
#include <boost/shared_ptr.hpp>
#include <dlib/matrix.h>
#include <dlib/string.h>
#include <boost/python/args.hpp>
#include <pybind11/pybind11.h>
using namespace dlib;
using namespace boost::python;
namespace py = pybind11;
using std::string;
using std::ostringstream;
......@@ -34,60 +33,59 @@ string matrix_double__str__(matrix<double>& c)
return trim(sout.str());
}
boost::shared_ptr<matrix<double> > make_matrix_from_size(long nr, long nc)
std::shared_ptr<matrix<double> > make_matrix_from_size(long nr, long nc)
{
if (nr < 0 || nc < 0)
{
PyErr_SetString( PyExc_IndexError, "Input dimensions can't be negative."
);
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_IndexError, "Input dimensions can't be negative."
);
throw py::error_already_set();
}
boost::shared_ptr<matrix<double> > temp(new matrix<double>(nr,nc));
auto temp = std::make_shared<matrix<double>>(nr,nc);
*temp = 0;
return temp;
}
boost::shared_ptr<matrix<double> > from_object(object obj)
std::shared_ptr<matrix<double> > from_object(py::object obj)
{
tuple s = extract<tuple>(obj.attr("shape"));
py::tuple s = obj.attr("shape").cast<py::tuple>();
if (len(s) != 2)
{
PyErr_SetString( PyExc_IndexError, "Input must be a matrix or some kind of 2D array."
);
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_IndexError, "Input must be a matrix or some kind of 2D array."
);
throw py::error_already_set();
}
const long nr = extract<long>(s[0]);
const long nc = extract<long>(s[1]);
boost::shared_ptr<matrix<double> > temp(new matrix<double>(nr,nc));
const long nr = s[0].cast<long>();
const long nc = s[1].cast<long>();
auto temp = std::make_shared<matrix<double>>(nr,nc);
for ( long r = 0; r < nr; ++r)
{
for (long c = 0; c < nc; ++c)
{
(*temp)(r,c) = extract<double>(obj[make_tuple(r,c)]);
(*temp)(r,c) = obj[py::make_tuple(r,c)].cast<double>();
}
}
return temp;
}
boost::shared_ptr<matrix<double> > from_list(list l)
std::shared_ptr<matrix<double> > from_list(py::list l)
{
const long nr = len(l);
if (extract<list>(l[0]).check())
const long nr = py::len(l);
if (py::isinstance<py::list>(l[0]))
{
const long nc = len(l[0]);
const long nc = py::len(l[0]);
// make sure all the other rows have the same length
for (long r = 1; r < nr; ++r)
pyassert(len(l[r]) == nc, "All rows of a matrix must have the same number of columns.");
pyassert(py::len(l[r]) == nc, "All rows of a matrix must have the same number of columns.");
boost::shared_ptr<matrix<double> > temp(new matrix<double>(nr,nc));
auto temp = std::make_shared<matrix<double>>(nr,nc);
for ( long r = 0; r < nr; ++r)
{
for (long c = 0; c < nc; ++c)
{
(*temp)(r,c) = extract<double>(l[r][c]);
(*temp)(r,c) = l[r].cast<py::list>()[c].cast<double>();
}
}
return temp;
......@@ -95,10 +93,10 @@ boost::shared_ptr<matrix<double> > from_list(list l)
else
{
// In this case we treat it like a column vector
boost::shared_ptr<matrix<double> > temp(new matrix<double>(nr,1));
auto temp = std::make_shared<matrix<double>>(nr,1);
for ( long r = 0; r < nr; ++r)
{
(*temp)(r) = extract<double>(l[r]);
(*temp)(r) = l[r].cast<double>();
}
return temp;
}
......@@ -123,9 +121,9 @@ void mat_row__setitem__(mat_row& c, long p, double val)
p = c.size + p; // negative index
}
if (p > c.size-1) {
PyErr_SetString( PyExc_IndexError, "3 index out of range"
);
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_IndexError, "3 index out of range"
);
throw py::error_already_set();
}
c.data[p] = val;
}
......@@ -156,9 +154,9 @@ double mat_row__getitem__(mat_row& m, long r)
r = m.size + r; // negative index
}
if (r > m.size-1 || r < 0) {
PyErr_SetString( PyExc_IndexError, "1 index out of range"
);
boost::python::throw_error_already_set();
PyErr_SetString( PyExc_IndexError, "1 index out of range"
);
throw py::error_already_set();
}
return m.data[r];
}
......@@ -170,40 +168,41 @@ mat_row matrix_double__getitem__(matrix<double>& m, long r)
}
if (r > m.nr()-1 || r < 0) {
PyErr_SetString( PyExc_IndexError, (string("2 index out of range, got ") + cast_to_string(r)).c_str()
);
boost::python::throw_error_already_set();
);
throw py::error_already_set();
}
return mat_row(&m(r,0),m.nc());
}
tuple get_matrix_size(matrix<double>& m)
py::tuple get_matrix_size(matrix<double>& m)
{
return make_tuple(m.nr(), m.nc());
return py::make_tuple(m.nr(), m.nc());
}
void bind_matrix()
void bind_matrix(py::module& m)
{
class_<mat_row>("_row")
py::class_<mat_row>(m, "_row")
.def("__len__", &mat_row__len__)
.def("__repr__", &mat_row__repr__)
.def("__str__", &mat_row__str__)
.def("__setitem__", &mat_row__setitem__)
.def("__getitem__", &mat_row__getitem__);
class_<matrix<double> >("matrix", "This object represents a dense 2D matrix of floating point numbers."
"Moreover, it binds directly to the C++ type dlib::matrix<double>.", init<>())
.def("__init__", make_constructor(&make_matrix_from_size))
.def("set_size", &matrix_set_size, (arg("rows"), arg("cols")), "Set the size of the matrix to the given number of rows and columns.")
.def("__init__", make_constructor(&from_object))
.def("__init__", make_constructor(&from_list))
py::class_<matrix<double>, std::shared_ptr<matrix<double>>>(m, "matrix",
"This object represents a dense 2D matrix of floating point numbers."
"Moreover, it binds directly to the C++ type dlib::matrix<double>.")
.def(py::init<>())
.def(py::init(&from_list))
.def(py::init(&from_object))
.def(py::init(&make_matrix_from_size))
.def("set_size", &matrix_set_size, py::arg("rows"), py::arg("cols"), "Set the size of the matrix to the given number of rows and columns.")
.def("__repr__", &matrix_double__repr__)
.def("__str__", &matrix_double__str__)
.def("nr", &matrix<double>::nr, "Return the number of rows in the matrix.")
.def("nc", &matrix<double>::nc, "Return the number of columns in the matrix.")
.def("__len__", &matrix_double__len__)
.def("__getitem__", &matrix_double__getitem__, with_custodian_and_ward_postcall<0,1>())
.add_property("shape", &get_matrix_size)
.def_pickle(serialize_pickle<matrix<double> >());
.def("__getitem__", &matrix_double__getitem__, py::keep_alive<0,1>())
.def_property_readonly("shape", &get_matrix_size)
.def(py::pickle(&getstate<matrix<double>>, &setstate<matrix<double>>));
}
#include <dlib/python.h>
#include <boost/python/args.hpp>
#include "dlib/pixel.h"
#include <dlib/image_transforms.h>
......@@ -10,11 +9,12 @@ dlib::rand rnd_jitter;
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
boost::python::list get_jitter_images(object img, size_t num_jitters = 1, bool disturb_colors = false)
py::list get_jitter_images(py::object img, size_t num_jitters = 1, bool disturb_colors = false)
{
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
......@@ -24,7 +24,7 @@ boost::python::list get_jitter_images(object img, size_t num_jitters = 1, bool d
assign_image(img_mat, numpy_rgb_image(img));
// The top level list (containing 1 or more images) to return to python
boost::python::list jitter_list;
py::list jitter_list;
size_t rows = num_rows(img_mat);
size_t cols = num_columns(img_mat);
......@@ -43,20 +43,18 @@ boost::python::list get_jitter_images(object img, size_t num_jitters = 1, bool d
npy_uint8 *outdata = (npy_uint8 *) PyArray_DATA((PyArrayObject*) arr);
memcpy(outdata, image_data(crop), rows * width_step(crop));
boost::python::handle<> handle(arr);
py::handle handle(arr);
// Append image to jittered image list
jitter_list.append(object(handle));
jitter_list.append(handle);
}
return jitter_list;
}
BOOST_PYTHON_FUNCTION_OVERLOADS(get_jitter_images_with_defaults, get_jitter_images, 1, 3)
// ----------------------------------------------------------------------------------------
boost::python::list get_face_chips (
object img,
py::list get_face_chips (
py::object img,
const std::vector<full_object_detection>& faces,
size_t size = 150,
float padding = 0.25
......@@ -69,7 +67,7 @@ boost::python::list get_face_chips (
throw dlib::error("No face were specified in the faces array.");
}
boost::python::list chips_list;
py::list chips_list;
std::vector<chip_details> dets;
for (auto& f : faces)
......@@ -88,16 +86,16 @@ boost::python::list get_face_chips (
PyObject *arr = PyArray_SimpleNew(3, dims, NPY_UINT8);
npy_uint8 *outdata = (npy_uint8 *) PyArray_DATA((PyArrayObject*) arr);
memcpy(outdata, image_data(chip), rows * width_step(chip));
boost::python::handle<> handle(arr);
py::handle handle(arr);
// Append image to chips list
chips_list.append(object(handle));
chips_list.append(handle);
}
return chips_list;
}
object get_face_chip (
object img,
py::object get_face_chip (
py::object img,
const full_object_detection& face,
size_t size = 150,
float padding = 0.25
......@@ -115,14 +113,10 @@ object get_face_chip (
PyObject *arr = PyArray_SimpleNew(3, dims, NPY_UINT8);
npy_uint8 *outdata = (npy_uint8 *) PyArray_DATA((PyArrayObject *) arr);
memcpy(outdata, image_data(chip), num_rows(chip) * width_step(chip));
boost::python::handle<> handle(arr);
return object(handle);
py::handle handle(arr);
return handle.cast<py::object>();
}
BOOST_PYTHON_FUNCTION_OVERLOADS(get_face_chip_with_defaults, get_face_chip, 2, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(get_face_chips_with_defaults, get_face_chips, 2, 4)
// ----------------------------------------------------------------------------------------
// we need this wonky stuff because different versions of numpy's import_array macro
......@@ -140,25 +134,24 @@ DLIB_NUMPY_IMPORT_ARRAY_RETURN_TYPE import_numpy_stuff()
DLIB_NUMPY_IMPORT_RETURN;
}
void bind_numpy_returns()
void bind_numpy_returns(py::module &m)
{
using boost::python::arg;
import_numpy_stuff();
def("jitter_image", &get_jitter_images, get_jitter_images_with_defaults(
m.def("jitter_image", &get_jitter_images,
"Takes an image and returns a list of jittered images."
"The returned list contains num_jitters images (default is 1)."
"If disturb_colors is set to True, the colors of the image are disturbed (default is False)",
(arg("img"), arg("num_jitters"), arg("disturb_colors"))
));
py::arg("img"), py::arg("num_jitters")=1, py::arg("disturb_colors")=false
);
def("get_face_chip", &get_face_chip, get_face_chip_with_defaults(
m.def("get_face_chip", &get_face_chip,
"Takes an image and a full_object_detection that references a face in that image and returns the face as a Numpy array representing the image. The face will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("face"), arg("size"), arg("padding"))
));
py::arg("img"), py::arg("face"), py::arg("size")=150, py::arg("padding")=0.25
);
def("get_face_chips", &get_face_chips, get_face_chips_with_defaults(
m.def("get_face_chips", &get_face_chips,
"Takes an image and a full_object_detections object that reference faces in that image and returns the faces as a list of Numpy arrays representing the image. The faces will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("faces"), arg("size"), arg("padding"))
));
py::arg("img"), py::arg("faces"), py::arg("size")=150, py::arg("padding")=0.25
);
}
#include <dlib/python.h>
#include <boost/python/args.hpp>
#include "dlib/pixel.h"
#include <dlib/image_transforms.h>
using namespace dlib;
using namespace std;
using namespace boost::python;
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
boost::python::list get_jitter_images(object img, size_t num_jitters = 1, bool disturb_colors = false)
py::list get_jitter_images(py::object img, size_t num_jitters = 1, bool disturb_colors = false)
{
throw dlib::error("jitter_image is only supported if you compiled dlib with numpy installed!");
}
BOOST_PYTHON_FUNCTION_OVERLOADS(get_jitter_images_with_defaults, get_jitter_images, 1, 3)
// ----------------------------------------------------------------------------------------
boost::python::list get_face_chips (
object img,
py::list get_face_chips (
py::object img,
const std::vector<full_object_detection>& faces,
size_t size = 150,
float padding = 0.25
......@@ -28,8 +25,8 @@ boost::python::list get_face_chips (
throw dlib::error("get_face_chips is only supported if you compiled dlib with numpy installed!");
}
object get_face_chip (
object img,
py::object get_face_chip (
py::object img,
const full_object_detection& face,
size_t size = 150,
float padding = 0.25
......@@ -38,30 +35,24 @@ object get_face_chip (
throw dlib::error("get_face_chip is only supported if you compiled dlib with numpy installed!");
}
BOOST_PYTHON_FUNCTION_OVERLOADS(get_face_chip_with_defaults, get_face_chip, 2, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(get_face_chips_with_defaults, get_face_chips, 2, 4)
// ----------------------------------------------------------------------------------------
void bind_numpy_returns()
void bind_numpy_returns(py::module &m)
{
using boost::python::arg;
def("jitter_image", &get_jitter_images, get_jitter_images_with_defaults(
m.def("jitter_image", &get_jitter_images,
"Takes an image and returns a list of jittered images."
"The returned list contains num_jitters images (default is 1)."
"If disturb_colors is set to True, the colors of the image are disturbed (default is False)",
(arg("img"), arg("num_jitters"), arg("disturb_colors"))
));
py::arg("img"), py::arg("num_jitters")=1, py::arg("disturb_colors")=false
);
def("get_face_chip", &get_face_chip, get_face_chip_with_defaults(
m.def("get_face_chip", &get_face_chip,
"Takes an image and a full_object_detection that references a face in that image and returns the face as a Numpy array representing the image. The face will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("face"), arg("size"), arg("padding"))
));
py::arg("img"), py::arg("face"), py::arg("size")=150, py::arg("padding")=0.25
);
def("get_face_chips", &get_face_chips, get_face_chips_with_defaults(
m.def("get_face_chips", &get_face_chips,
"Takes an image and a full_object_detections object that reference faces in that image and returns the faces as a list of Numpy arrays representing the image. The faces will be rotated upright and scaled to 150x150 pixels or with the optional specified size and padding.",
(arg("img"), arg("faces"), arg("size"), arg("padding"))
));
}
\ No newline at end of file
py::arg("img"), py::arg("faces"), py::arg("size")=150, py::arg("padding")=0.25
);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment