Commit 43e5f42e authored by Davis King's avatar Davis King

Added numpy_image templated class that makes a numpy array

conform to dlib's generic image interface.  This makes dealing with
numpy images in pybind11 modules much nicer.
parent f6651c2c
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include "python/pybind_utils.h" #include "python/pybind_utils.h"
#include "python/pyassert.h" #include "python/pyassert.h"
#include "python/serialize_pickle.h" #include "python/serialize_pickle.h"
#include "python/numpy.h"
#include "python/numpy_image.h" #include "python/numpy_image.h"
#endif // DLIB_PYTHoN_TOP_ #endif // DLIB_PYTHoN_TOP_
......
// Copyright (C) 2014 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_PYTHON_NuMPY_Hh_
#define DLIB_PYTHON_NuMPY_Hh_
#include <pybind11/pybind11.h>
#include <dlib/error.h>
#include <dlib/algs.h>
#include <dlib/string.h>
#include <dlib/array.h>
#include <dlib/pixel.h>
namespace py = pybind11;
// ----------------------------------------------------------------------------------------
template <typename TT>
void validate_numpy_array_type (
const py::object& obj
)
{
const char ch = obj.attr("dtype").attr("char").cast<char>();
using T = typename dlib::pixel_traits<TT>::basic_pixel_type;
if (dlib::is_same_type<T,double>::value)
{
if (ch != 'd')
throw dlib::error("Expected numpy.ndarray of float64");
}
else if (dlib::is_same_type<T,float>::value)
{
if (ch != 'f')
throw dlib::error("Expected numpy.ndarray of float32");
}
else if (dlib::is_same_type<T,dlib::int16>::value)
{
if (ch != 'h')
throw dlib::error("Expected numpy.ndarray of int16");
}
else if (dlib::is_same_type<T,dlib::uint16>::value)
{
if (ch != 'H')
throw dlib::error("Expected numpy.ndarray of uint16");
}
else if (dlib::is_same_type<T,dlib::int32>::value)
{
if (ch != 'i')
throw dlib::error("Expected numpy.ndarray of int32");
}
else if (dlib::is_same_type<T,dlib::uint32>::value)
{
if (ch != 'I')
throw dlib::error("Expected numpy.ndarray of uint32");
}
else if (dlib::is_same_type<T,unsigned char>::value)
{
if (ch != 'B')
throw dlib::error("Expected numpy.ndarray of uint8");
}
else if (dlib::is_same_type<T,signed char>::value)
{
if (ch != 'b')
throw dlib::error("Expected numpy.ndarray of int8");
}
else
{
throw dlib::error("validate_numpy_array_type() called with unsupported type.");
}
}
// ----------------------------------------------------------------------------------------
template <int dims>
void get_numpy_ndarray_shape (
const py::object& obj,
long (&shape)[dims]
)
/*!
ensures
- stores the shape of the array into #shape.
- the dimension of the given numpy array is not greater than #dims.
!*/
{
Py_buffer pybuf;
if (PyObject_GetBuffer(obj.ptr(), &pybuf, PyBUF_STRIDES ))
throw dlib::error("Expected numpy.ndarray with shape set.");
try
{
if (pybuf.ndim > dims)
throw dlib::error("Expected array with " + dlib::cast_to_string(dims) + " dimensions.");
for (int i = 0; i < dims; ++i)
{
if (i < pybuf.ndim)
shape[i] = pybuf.shape[i];
else
shape[i] = 1;
}
}
catch(...)
{
PyBuffer_Release(&pybuf);
throw;
}
PyBuffer_Release(&pybuf);
}
// ----------------------------------------------------------------------------------------
template <typename T, int dims>
void get_numpy_ndarray_parts (
py::object& obj,
T*& data,
dlib::array<T>& contig_buf,
long (&shape)[dims]
)
/*!
ensures
- extracts the pointer to the data from the given numpy ndarray. Stores the shape
of the array into #shape.
- the dimension of the given numpy array is not greater than #dims.
- #shape[#dims-1] == pixel_traits<T>::num when #dims is greater than 2
!*/
{
Py_buffer pybuf;
if (PyObject_GetBuffer(obj.ptr(), &pybuf, PyBUF_STRIDES | PyBUF_WRITABLE ))
throw dlib::error("Expected writable numpy.ndarray with shape set.");
try
{
validate_numpy_array_type<T>(obj);
if (pybuf.ndim > dims)
throw dlib::error("Expected array with " + dlib::cast_to_string(dims) + " dimensions.");
get_numpy_ndarray_shape(obj, shape);
if (dlib::pixel_traits<T>::num > 1 && dlib::pixel_traits<T>::num != shape[dims-1])
throw dlib::error("Expected numpy.ndarray with " + dlib::cast_to_string(dlib::pixel_traits<T>::num) + " channels.");
if (PyBuffer_IsContiguous(&pybuf, 'C'))
data = (T*)pybuf.buf;
else
{
contig_buf.resize(pybuf.len);
if (PyBuffer_ToContiguous(&contig_buf[0], &pybuf, pybuf.len, 'C'))
throw dlib::error("Can't copy numpy.ndarray to a contiguous buffer.");
data = &contig_buf[0];
}
}
catch(...)
{
PyBuffer_Release(&pybuf);
throw;
}
PyBuffer_Release(&pybuf);
}
// ----------------------------------------------------------------------------------------
template <typename T, int dims>
void get_numpy_ndarray_parts (
const py::object& obj,
const T*& data,
dlib::array<T>& contig_buf,
long (&shape)[dims]
)
/*!
ensures
- extracts the pointer to the data from the given numpy ndarray. Stores the shape
of the array into #shape.
- the dimension of the given numpy array is not greater than #dims.
- #shape[#dims-1] == pixel_traits<T>::num when #dims is greater than 2
!*/
{
Py_buffer pybuf;
if (PyObject_GetBuffer(obj.ptr(), &pybuf, PyBUF_STRIDES ))
throw dlib::error("Expected numpy.ndarray with shape set.");
try
{
validate_numpy_array_type<T>(obj);
if (pybuf.ndim > dims)
throw dlib::error("Expected array with " + dlib::cast_to_string(dims) + " dimensions.");
get_numpy_ndarray_shape(obj, shape);
if (dlib::pixel_traits<T>::num > 1 && dlib::pixel_traits<T>::num != shape[dims-1])
throw dlib::error("Expected numpy.ndarray with " + dlib::cast_to_string(dlib::pixel_traits<T>::num) + " channels.");
if (PyBuffer_IsContiguous(&pybuf, 'C'))
data = (const T*)pybuf.buf;
else
{
contig_buf.resize(pybuf.len);
if (PyBuffer_ToContiguous(&contig_buf[0], &pybuf, pybuf.len, 'C'))
throw dlib::error("Can't copy numpy.ndarray to a contiguous buffer.");
data = &contig_buf[0];
}
}
catch(...)
{
PyBuffer_Release(&pybuf);
throw;
}
PyBuffer_Release(&pybuf);
}
// ----------------------------------------------------------------------------------------
#endif // DLIB_PYTHON_NuMPY_Hh_
This diff is collapsed.
...@@ -6,7 +6,7 @@ set(USE_SSE4_INSTRUCTIONS ON CACHE BOOL "Use SSE4 instructions") ...@@ -6,7 +6,7 @@ set(USE_SSE4_INSTRUCTIONS ON CACHE BOOL "Use SSE4 instructions")
# Set this to disable link time optimization. The only reason for # Set this to disable link time optimization. The only reason for
# doing this to make the compile faster which is nice when developing # doing this to make the compile faster which is nice when developing
# new modules. # new modules.
#set(PYBIND11_LTO_CXX_FLAGS "") set(PYBIND11_LTO_CXX_FLAGS "")
# Avoid cmake warnings about changes in behavior of some Mac OS X path # Avoid cmake warnings about changes in behavior of some Mac OS X path
......
...@@ -26,7 +26,7 @@ public: ...@@ -26,7 +26,7 @@ public:
} }
std::vector<mmod_rect> detect ( std::vector<mmod_rect> detect (
py::object pyimage, py::array pyimage,
const int upsample_num_times const int upsample_num_times
) )
{ {
...@@ -35,10 +35,10 @@ public: ...@@ -35,10 +35,10 @@ public:
// Copy the data into dlib based objects // Copy the data into dlib based objects
matrix<rgb_pixel> image; matrix<rgb_pixel> image;
if (is_gray_python_image(pyimage)) if (is_image<unsigned char>(pyimage))
assign_image(image, numpy_gray_image(pyimage)); assign_image(image, numpy_image<unsigned char>(pyimage));
else if (is_rgb_python_image(pyimage)) else if (is_image<rgb_pixel>(pyimage))
assign_image(image, numpy_rgb_image(pyimage)); assign_image(image, numpy_image<rgb_pixel>(pyimage));
else else
throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); throw dlib::error("Unsupported image type, must be 8bit gray or RGB image.");
...@@ -63,25 +63,25 @@ public: ...@@ -63,25 +63,25 @@ public:
return rects; return rects;
} }
std::vector<std::vector<mmod_rect> > detect_mult ( std::vector<std::vector<mmod_rect>> detect_mult (
py::list imgs, py::list imgs,
const int upsample_num_times, const int upsample_num_times,
const int batch_size = 128 const int batch_size = 128
) )
{ {
pyramid_down<2> pyr; pyramid_down<2> pyr;
std::vector<matrix<rgb_pixel> > dimgs; std::vector<matrix<rgb_pixel>> dimgs;
dimgs.reserve(len(imgs)); dimgs.reserve(len(imgs));
for(int i = 0; i < len(imgs); i++) for(int i = 0; i < len(imgs); i++)
{ {
// Copy the data into dlib based objects // Copy the data into dlib based objects
matrix<rgb_pixel> image; matrix<rgb_pixel> image;
py::object tmp = imgs[i].cast<py::object>(); py::array tmp = imgs[i].cast<py::array>();
if (is_gray_python_image(tmp)) if (is_image<unsigned char>(tmp))
assign_image(image, numpy_gray_image(tmp)); assign_image(image, numpy_image<unsigned char>(tmp));
else if (is_rgb_python_image(tmp)) else if (is_image<rgb_pixel>(tmp))
assign_image(image, numpy_rgb_image(tmp)); assign_image(image, numpy_image<rgb_pixel>(tmp));
else else
throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); throw dlib::error("Unsupported image type, must be 8bit gray or RGB image.");
...@@ -89,16 +89,12 @@ public: ...@@ -89,16 +89,12 @@ public:
{ {
pyramid_up(image); pyramid_up(image);
} }
dimgs.push_back(image); dimgs.emplace_back(std::move(image));
} }
for(int i = 1; i < dimgs.size(); i++) for(int i = 1; i < dimgs.size(); i++)
{ {
if if (dimgs[i - 1].nc() != dimgs[i].nc() || dimgs[i - 1].nr() != dimgs[i].nr())
(
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."); throw dlib::error("Images in list must all have the same dimensions.");
} }
......
...@@ -6,46 +6,32 @@ ...@@ -6,46 +6,32 @@
#include "opaque_types.h" #include "opaque_types.h"
#include <dlib/python.h> #include <dlib/python.h>
#include <dlib/pixel.h> #include <dlib/pixel.h>
#include <dlib/python/numpy_image.h>
using namespace dlib; using namespace dlib;
using namespace std; using namespace std;
namespace py = pybind11; namespace py = pybind11;
template <typename dest_image_type>
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 <typename image_array, typename param_type> template <typename image_array, typename param_type>
void images_and_nested_params_to_dlib( void images_and_nested_params_to_dlib(
const py::object& pyimages, const py::object& pyimages,
const py::object& pyparams, const py::object& pyparams,
image_array& images, image_array& images,
std::vector<std::vector<param_type> >& params std::vector<std::vector<param_type>>& params
) )
{ {
// Now copy the data into dlib based objects. // Now copy the data into dlib based objects.
py::iterator image_it = pyimages.begin(); py::iterator image_it = pyimages.begin();
py::iterator params_it = pyparams.begin(); py::iterator params_it = pyparams.begin();
for (unsigned long image_idx = 0; for (unsigned long image_idx = 0; image_it != pyimages.end() && params_it != pyparams.end(); ++image_it, ++params_it, ++image_idx)
image_it != pyimages.end()
&& params_it != pyparams.end();
++image_it, ++params_it, ++image_idx)
{ {
for (py::iterator param_it = params_it->begin(); for (py::iterator param_it = params_it->begin(); param_it != params_it->end(); ++param_it)
param_it != params_it->end(); params[image_idx].push_back(param_it->cast<param_type>());
++param_it)
params[image_idx].push_back(param_it->cast<param_type>());
pyimage_to_dlib_image(image_it->cast<py::object>(), images[image_idx]); images[image_idx] = image_it->cast<py::object>();
} }
} }
......
...@@ -15,17 +15,17 @@ namespace py = pybind11; ...@@ -15,17 +15,17 @@ namespace py = pybind11;
void start_track ( void start_track (
correlation_tracker& tracker, correlation_tracker& tracker,
py::object img, py::array img,
const drectangle& bounding_box const drectangle& bounding_box
) )
{ {
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
tracker.start_track(numpy_gray_image(img), bounding_box); tracker.start_track(numpy_image<unsigned char>(img), bounding_box);
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
tracker.start_track(numpy_rgb_image(img), bounding_box); tracker.start_track(numpy_image<rgb_pixel>(img), bounding_box);
} }
else else
{ {
...@@ -35,7 +35,7 @@ void start_track ( ...@@ -35,7 +35,7 @@ void start_track (
void start_track_rec ( void start_track_rec (
correlation_tracker& tracker, correlation_tracker& tracker,
py::object img, py::array img,
const rectangle& bounding_box const rectangle& bounding_box
) )
{ {
...@@ -45,16 +45,16 @@ void start_track_rec ( ...@@ -45,16 +45,16 @@ void start_track_rec (
double update ( double update (
correlation_tracker& tracker, correlation_tracker& tracker,
py::object img py::array img
) )
{ {
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
return tracker.update(numpy_gray_image(img)); return tracker.update(numpy_image<unsigned char>(img));
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
return tracker.update(numpy_rgb_image(img)); return tracker.update(numpy_image<rgb_pixel>(img));
} }
else else
{ {
...@@ -64,17 +64,17 @@ double update ( ...@@ -64,17 +64,17 @@ double update (
double update_guess ( double update_guess (
correlation_tracker& tracker, correlation_tracker& tracker,
py::object img, py::array img,
const drectangle& bounding_box const drectangle& bounding_box
) )
{ {
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
return tracker.update(numpy_gray_image(img), bounding_box); return tracker.update(numpy_image<unsigned char>(img), bounding_box);
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
return tracker.update(numpy_rgb_image(img), bounding_box); return tracker.update(numpy_image<rgb_pixel>(img), bounding_box);
} }
else else
{ {
...@@ -84,7 +84,7 @@ double update_guess ( ...@@ -84,7 +84,7 @@ double update_guess (
double update_guess_rec ( double update_guess_rec (
correlation_tracker& tracker, correlation_tracker& tracker,
py::object img, py::array img,
const rectangle& bounding_box const rectangle& bounding_box
) )
{ {
......
...@@ -32,7 +32,7 @@ public: ...@@ -32,7 +32,7 @@ public:
} }
matrix<double,0,1> compute_face_descriptor ( matrix<double,0,1> compute_face_descriptor (
py::object img, numpy_image<rgb_pixel> img,
const full_object_detection& face, const full_object_detection& face,
const int num_jitters const int num_jitters
) )
...@@ -42,13 +42,11 @@ public: ...@@ -42,13 +42,11 @@ public:
} }
std::vector<matrix<double,0,1>> compute_face_descriptors ( std::vector<matrix<double,0,1>> compute_face_descriptors (
py::object img, numpy_image<rgb_pixel> img,
const std::vector<full_object_detection>& faces, const std::vector<full_object_detection>& faces,
const int num_jitters const int num_jitters
) )
{ {
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
for (auto& f : faces) for (auto& f : faces)
{ {
...@@ -61,7 +59,7 @@ public: ...@@ -61,7 +59,7 @@ public:
for (auto& f : faces) for (auto& f : faces)
dets.push_back(get_face_chip_details(f, 150, 0.25)); dets.push_back(get_face_chip_details(f, 150, 0.25));
dlib::array<matrix<rgb_pixel>> face_chips; dlib::array<matrix<rgb_pixel>> face_chips;
extract_image_chips(numpy_rgb_image(img), dets, face_chips); extract_image_chips(img, dets, face_chips);
std::vector<matrix<double,0,1>> face_descriptors; std::vector<matrix<double,0,1>> face_descriptors;
face_descriptors.reserve(face_chips.size()); face_descriptors.reserve(face_chips.size());
...@@ -161,22 +159,20 @@ py::list chinese_whispers_clustering(py::list descriptors, float threshold) ...@@ -161,22 +159,20 @@ py::list chinese_whispers_clustering(py::list descriptors, float threshold)
} }
void save_face_chips ( void save_face_chips (
py::object img, numpy_image<rgb_pixel> img,
const std::vector<full_object_detection>& faces, const std::vector<full_object_detection>& faces,
const std::string& chip_filename, const std::string& chip_filename,
size_t size = 150, size_t size = 150,
float padding = 0.25 float padding = 0.25
) )
{ {
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
int num_faces = faces.size(); int num_faces = faces.size();
std::vector<chip_details> dets; std::vector<chip_details> dets;
for (auto& f : faces) for (auto& f : faces)
dets.push_back(get_face_chip_details(f, size, padding)); dets.push_back(get_face_chip_details(f, size, padding));
dlib::array<matrix<rgb_pixel>> face_chips; dlib::array<matrix<rgb_pixel>> face_chips;
extract_image_chips(numpy_rgb_image(img), dets, face_chips); extract_image_chips(numpy_image<rgb_pixel>(img), dets, face_chips);
int i=0; int i=0;
for (auto& chip : face_chips) for (auto& chip : face_chips)
{ {
...@@ -195,7 +191,7 @@ void save_face_chips ( ...@@ -195,7 +191,7 @@ void save_face_chips (
} }
void save_face_chip ( void save_face_chip (
py::object img, numpy_image<rgb_pixel> img,
const full_object_detection& face, const full_object_detection& face,
const std::string& chip_filename, const std::string& chip_filename,
size_t size = 150, size_t size = 150,
...@@ -204,7 +200,6 @@ void save_face_chip ( ...@@ -204,7 +200,6 @@ void save_face_chip (
{ {
std::vector<full_object_detection> faces(1, face); std::vector<full_object_detection> faces(1, face);
save_face_chips(img, faces, chip_filename, size, padding); save_face_chips(img, faces, chip_filename, size, padding);
return;
} }
void bind_face_recognition(py::module &m) void bind_face_recognition(py::module &m)
......
...@@ -35,13 +35,13 @@ void image_window_set_image_simple_detector_py ( ...@@ -35,13 +35,13 @@ void image_window_set_image_simple_detector_py (
void image_window_set_image ( void image_window_set_image (
image_window& win, image_window& win,
py::object img py::array img
) )
{ {
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
return win.set_image(numpy_gray_image(img)); return win.set_image(numpy_image<unsigned char>(img));
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
return win.set_image(numpy_rgb_image(img)); return win.set_image(numpy_image<rgb_pixel>(img));
else else
throw dlib::error("Unsupported image type, must be 8bit gray or RGB image."); throw dlib::error("Unsupported image type, must be 8bit gray or RGB image.");
} }
...@@ -74,14 +74,14 @@ void add_overlay_parts ( ...@@ -74,14 +74,14 @@ void add_overlay_parts (
win.add_overlay(render_face_detections(detection, color)); win.add_overlay(render_face_detections(detection, color));
} }
std::shared_ptr<image_window> make_image_window_from_image(py::object img) std::shared_ptr<image_window> make_image_window_from_image(py::array img)
{ {
auto win = std::make_shared<image_window>(); auto win = std::make_shared<image_window>();
image_window_set_image(*win, img); image_window_set_image(*win, img);
return win; return win;
} }
std::shared_ptr<image_window> make_image_window_from_image_and_title(py::object img, const string& title) std::shared_ptr<image_window> make_image_window_from_image_and_title(py::array img, const string& title)
{ {
auto win = std::make_shared<image_window>(); auto win = std::make_shared<image_window>();
image_window_set_image(*win, img); image_window_set_image(*win, img);
......
...@@ -10,36 +10,14 @@ using namespace std; ...@@ -10,36 +10,14 @@ using namespace std;
namespace py = pybind11; namespace py = pybind11;
py::array_t<uint8_t> convert_to_numpy(matrix<rgb_pixel>&& rgb_image)
{
const size_t dtype_size = sizeof(uint8_t);
const auto rows = static_cast<const size_t>(num_rows(rgb_image));
const auto cols = static_cast<const size_t>(num_columns(rgb_image));
const size_t channels = 3;
const size_t image_size = dtype_size * rows * cols * channels;
unique_ptr<rgb_pixel[]> arr_ptr = rgb_image.steal_memory();
uint8_t* arr = (uint8_t *) arr_ptr.release();
return pybind11::array_t<uint8_t>(
{rows, cols, channels}, // shape
{dtype_size * cols * channels, dtype_size * channels, dtype_size}, // strides
arr, // pointer
pybind11::capsule{
arr, [](void *arr_p) {
delete[] reinterpret_cast<uint8_t *>(arr_p);
}
}
);
}
// -------------------------------- Basic Image IO ---------------------------------------- // -------------------------------- Basic Image IO ----------------------------------------
py::array_t<uint8_t> load_rgb_image (const std::string &path) numpy_image<rgb_pixel> load_rgb_image (const std::string &path)
{ {
matrix<rgb_pixel> img; numpy_image<rgb_pixel> img;
load_image(img, path); load_image(img, path);
return convert_to_numpy(std::move(img)); return img;
} }
bool has_ending (std::string const full_string, std::string const &ending) { bool has_ending (std::string const full_string, std::string const &ending) {
...@@ -50,22 +28,21 @@ bool has_ending (std::string const full_string, std::string const &ending) { ...@@ -50,22 +28,21 @@ bool has_ending (std::string const full_string, std::string const &ending) {
} }
} }
void save_rgb_image(py::object img, const std::string &path) // ----------------------------------------------------------------------------------------
{
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
void save_rgb_image(numpy_image<rgb_pixel> img, const std::string &path)
{
std::string lowered_path = path; std::string lowered_path = path;
std::transform(lowered_path.begin(), lowered_path.end(), lowered_path.begin(), ::tolower); std::transform(lowered_path.begin(), lowered_path.end(), lowered_path.begin(), ::tolower);
if(has_ending(lowered_path, ".bmp")) { if(has_ending(lowered_path, ".bmp")) {
save_bmp(numpy_rgb_image(img), path); save_bmp(img, path);
} else if(has_ending(lowered_path, ".dng")) { } else if(has_ending(lowered_path, ".dng")) {
save_dng(numpy_rgb_image(img), path); save_dng(img, path);
} else if(has_ending(lowered_path, ".png")) { } else if(has_ending(lowered_path, ".png")) {
save_png(numpy_rgb_image(img), path); save_png(img, path);
} else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) { } else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) {
save_jpeg(numpy_rgb_image(img), path); save_jpeg(img, path);
} else { } else {
throw dlib::error("Unsupported image type, image path must end with one of [.bmp, .png, .dng, .jpg, .jpeg]"); throw dlib::error("Unsupported image type, image path must end with one of [.bmp, .png, .dng, .jpg, .jpeg]");
} }
...@@ -74,31 +51,22 @@ void save_rgb_image(py::object img, const std::string &path) ...@@ -74,31 +51,22 @@ void save_rgb_image(py::object img, const std::string &path)
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
py::list get_jitter_images(py::object img, size_t num_jitters = 1, bool disturb_colors = false) py::list get_jitter_images(numpy_image<rgb_pixel> img, size_t num_jitters = 1, bool disturb_colors = false)
{ {
static dlib::rand rnd_jitter; static dlib::rand rnd_jitter;
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
// Convert the image to matrix<rgb_pixel> for processing
matrix<rgb_pixel> img_mat;
assign_image(img_mat, numpy_rgb_image(img));
// The top level list (containing 1 or more images) to return to python // The top level list (containing 1 or more images) to return to python
py::list jitter_list; py::list jitter_list;
for (int i = 0; i < num_jitters; ++i) { for (int i = 0; i < num_jitters; ++i) {
// Get a jittered crop // Get a jittered crop
matrix<rgb_pixel> crop = dlib::jitter_image(img_mat, rnd_jitter); numpy_image<rgb_pixel> crop = dlib::jitter_image(img, rnd_jitter);
// If required disturb colors of the image // If required disturb colors of the image
if(disturb_colors) if(disturb_colors)
dlib::disturb_colors(crop, rnd_jitter); dlib::disturb_colors(crop, rnd_jitter);
// Convert image to Numpy array
py::array_t<uint8_t> arr = convert_to_numpy(std::move(crop));
// Append image to jittered image list // Append image to jittered image list
jitter_list.append(arr); jitter_list.append(crop);
} }
return jitter_list; return jitter_list;
...@@ -107,14 +75,12 @@ py::list get_jitter_images(py::object img, size_t num_jitters = 1, bool disturb_ ...@@ -107,14 +75,12 @@ py::list get_jitter_images(py::object img, size_t num_jitters = 1, bool disturb_
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
py::list get_face_chips ( py::list get_face_chips (
py::object img, numpy_image<rgb_pixel> img,
const std::vector<full_object_detection>& faces, const std::vector<full_object_detection>& faces,
size_t size = 150, size_t size = 150,
float padding = 0.25 float padding = 0.25
) )
{ {
if (!is_rgb_python_image(img))
throw dlib::error("Unsupported image type, must be RGB image.");
if (faces.size() < 1) { if (faces.size() < 1) {
throw dlib::error("No face were specified in the faces array."); throw dlib::error("No face were specified in the faces array.");
...@@ -125,33 +91,27 @@ py::list get_face_chips ( ...@@ -125,33 +91,27 @@ py::list get_face_chips (
std::vector<chip_details> dets; std::vector<chip_details> dets;
for (auto& f : faces) for (auto& f : faces)
dets.push_back(get_face_chip_details(f, size, padding)); dets.push_back(get_face_chip_details(f, size, padding));
dlib::array<matrix<rgb_pixel>> face_chips; dlib::array<numpy_image<rgb_pixel>> face_chips;
extract_image_chips(numpy_rgb_image(img), dets, face_chips); extract_image_chips(img, dets, face_chips);
for (auto& chip : face_chips) for (auto& chip : face_chips)
{ {
// Convert image to Numpy array
py::array_t<uint8_t> arr = convert_to_numpy(std::move(chip));
// Append image to chips list // Append image to chips list
chips_list.append(arr); chips_list.append(chip);
} }
return chips_list; return chips_list;
} }
py::array_t<uint8_t> get_face_chip ( numpy_image<rgb_pixel> get_face_chip (
py::object img, numpy_image<rgb_pixel> img,
const full_object_detection& face, const full_object_detection& face,
size_t size = 150, size_t size = 150,
float padding = 0.25 float padding = 0.25
) )
{ {
if (!is_rgb_python_image(img)) numpy_image<rgb_pixel> chip;
throw dlib::error("Unsupported image type, must be RGB image."); extract_image_chip(img, get_face_chip_details(face, size, padding), chip);
return chip;
matrix<rgb_pixel> chip;
extract_image_chip(numpy_rgb_image(img), get_face_chip_details(face, size, padding), chip);
return convert_to_numpy(std::move(chip));
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
......
...@@ -37,8 +37,8 @@ inline simple_object_detector_py train_simple_object_detector_on_images_py ( ...@@ -37,8 +37,8 @@ inline simple_object_detector_py train_simple_object_detector_on_images_py (
throw dlib::error("The length of the boxes list must match the length of the images list."); throw dlib::error("The length of the boxes list must match the length of the images list.");
// We never have any ignore boxes for this version of the API. // We never have any ignore boxes for this version of the API.
std::vector<std::vector<rectangle> > ignore(num_images), boxes(num_images); std::vector<std::vector<rectangle>> ignore(num_images), boxes(num_images);
dlib::array<array2d<rgb_pixel> > images(num_images); dlib::array<numpy_image<rgb_pixel>> images(num_images);
images_and_nested_params_to_dlib(pyimages, pyboxes, images, boxes); images_and_nested_params_to_dlib(pyimages, pyboxes, images, boxes);
return train_simple_object_detector_on_images("", images, boxes, ignore, options); return train_simple_object_detector_on_images("", images, boxes, ignore, options);
...@@ -56,8 +56,8 @@ inline simple_test_results test_simple_object_detector_with_images_py ( ...@@ -56,8 +56,8 @@ inline simple_test_results test_simple_object_detector_with_images_py (
throw dlib::error("The length of the boxes list must match the length of the images list."); throw dlib::error("The length of the boxes list must match the length of the images list.");
// We never have any ignore boxes for this version of the API. // We never have any ignore boxes for this version of the API.
std::vector<std::vector<rectangle> > ignore(num_images), boxes(num_images); std::vector<std::vector<rectangle>> ignore(num_images), boxes(num_images);
dlib::array<array2d<rgb_pixel> > images(num_images); dlib::array<numpy_image<rgb_pixel>> images(num_images);
images_and_nested_params_to_dlib(pyimages, pyboxes, images, boxes); images_and_nested_params_to_dlib(pyimages, pyboxes, images, boxes);
return test_simple_object_detector_with_images(images, upsampling_amount, boxes, ignore, detector); return test_simple_object_detector_with_images(images, upsampling_amount, boxes, ignore, detector);
...@@ -86,22 +86,13 @@ inline simple_test_results test_simple_object_detector_py_with_images_py ( ...@@ -86,22 +86,13 @@ inline simple_test_results test_simple_object_detector_py_with_images_py (
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
inline void find_candidate_object_locations_py ( inline void find_candidate_object_locations_py (
py::object pyimage, py::array pyimage,
py::list& pyboxes, py::list& pyboxes,
py::tuple pykvals, py::tuple pykvals,
unsigned long min_size, unsigned long min_size,
unsigned long max_merging_iterations unsigned long max_merging_iterations
) )
{ {
// Copy the data into dlib based objects
array2d<rgb_pixel> 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.");
if (py::len(pykvals) != 3) if (py::len(pykvals) != 3)
throw dlib::error("kvals must be a tuple with three elements for start, end, num."); throw dlib::error("kvals must be a tuple with three elements for start, end, num.");
...@@ -117,7 +108,12 @@ inline void find_candidate_object_locations_py ( ...@@ -117,7 +108,12 @@ inline void find_candidate_object_locations_py (
for (long i = 0; i < count; ++i) for (long i = 0; i < count; ++i)
rects.push_back(pyboxes[i].cast<rectangle>()); rects.push_back(pyboxes[i].cast<rectangle>());
// Find candidate objects // Find candidate objects
find_candidate_object_locations(image, rects, kvals, min_size, max_merging_iterations); if (is_image<unsigned char>(pyimage))
find_candidate_object_locations(numpy_image<unsigned char>(pyimage), rects, kvals, min_size, max_merging_iterations);
else if (is_image<rgb_pixel>(pyimage))
find_candidate_object_locations(numpy_image<rgb_pixel>(pyimage), rects, kvals, min_size, max_merging_iterations);
else
throw dlib::error("Unsupported image type, must be 8bit gray or RGB image.");
// Collect boxes containing candidate objects // Collect boxes containing candidate objects
std::vector<rectangle>::iterator iter; std::vector<rectangle>::iterator iter;
......
...@@ -17,18 +17,18 @@ namespace py = pybind11; ...@@ -17,18 +17,18 @@ namespace py = pybind11;
full_object_detection run_predictor ( full_object_detection run_predictor (
shape_predictor& predictor, shape_predictor& predictor,
py::object img, py::array img,
py::object rect py::object rect
) )
{ {
rectangle box = rect.cast<rectangle>(); rectangle box = rect.cast<rectangle>();
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
return predictor(numpy_gray_image(img), box); return predictor(numpy_image<unsigned char>(img), box);
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
return predictor(numpy_rgb_image(img), box); return predictor(numpy_image<rgb_pixel>(img), box);
} }
else else
{ {
...@@ -97,7 +97,7 @@ inline shape_predictor train_shape_predictor_on_images_py ( ...@@ -97,7 +97,7 @@ inline shape_predictor train_shape_predictor_on_images_py (
throw dlib::error("The length of the detections list must match the length of the images list."); throw dlib::error("The length of the detections list must match the length of the images list.");
std::vector<std::vector<full_object_detection> > detections(num_images); std::vector<std::vector<full_object_detection> > detections(num_images);
dlib::array<array2d<unsigned char> > images(num_images); dlib::array<numpy_image<unsigned char>> images(num_images);
images_and_nested_params_to_dlib(pyimages, pydetections, images, detections); images_and_nested_params_to_dlib(pyimages, pydetections, images, detections);
return train_shape_predictor_on_images(images, detections, options); return train_shape_predictor_on_images(images, detections, options);
...@@ -123,7 +123,7 @@ inline double test_shape_predictor_with_images_py ( ...@@ -123,7 +123,7 @@ inline double test_shape_predictor_with_images_py (
std::vector<std::vector<double> > scales; std::vector<std::vector<double> > scales;
if (num_scales > 0) if (num_scales > 0)
scales.resize(num_scales); scales.resize(num_scales);
dlib::array<array2d<unsigned char> > images(num_images); dlib::array<numpy_image<unsigned char>> images(num_images);
// Now copy the data into dlib based objects so we can call the testing routine. // Now copy the data into dlib based objects so we can call the testing routine.
for (unsigned long i = 0; i < num_images; ++i) for (unsigned long i = 0; i < num_images; ++i)
...@@ -134,14 +134,12 @@ inline double test_shape_predictor_with_images_py ( ...@@ -134,14 +134,12 @@ inline double test_shape_predictor_with_images_py (
++det_it) ++det_it)
detections[i].push_back(det_it->cast<full_object_detection>()); detections[i].push_back(det_it->cast<full_object_detection>());
pyimage_to_dlib_image(pyimages[i], images[i]); images[i] = pyimages[i];
if (num_scales > 0) if (num_scales > 0)
{ {
if (num_boxes != py::len(pyscales[i])) if (num_boxes != py::len(pyscales[i]))
throw dlib::error("The length of the scales list must match the length of the detections list."); throw dlib::error("The length of the scales list must match the length of the detections list.");
for (py::iterator scale_it = pyscales[i].begin(); for (py::iterator scale_it = pyscales[i].begin(); scale_it != pyscales[i].end(); ++scale_it)
scale_it != pyscales[i].end();
++scale_it)
scales[i].push_back(scale_it->cast<double>()); scales[i].push_back(scale_it->cast<double>());
} }
} }
......
...@@ -37,7 +37,7 @@ namespace dlib ...@@ -37,7 +37,7 @@ namespace dlib
inline std::vector<dlib::rectangle> run_detector_with_upscale1 ( inline std::vector<dlib::rectangle> run_detector_with_upscale1 (
dlib::simple_object_detector& detector, dlib::simple_object_detector& detector,
py::object img, py::array img,
const unsigned int upsampling_amount, const unsigned int upsampling_amount,
const double adjust_threshold, const double adjust_threshold,
std::vector<double>& detection_confidences, std::vector<double>& detection_confidences,
...@@ -49,19 +49,19 @@ namespace dlib ...@@ -49,19 +49,19 @@ namespace dlib
std::vector<rectangle> rectangles; std::vector<rectangle> rectangles;
std::vector<rect_detection> rect_detections; std::vector<rect_detection> rect_detections;
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
array2d<unsigned char> temp; array2d<unsigned char> temp;
if (upsampling_amount == 0) if (upsampling_amount == 0)
{ {
detector(numpy_gray_image(img), rect_detections, adjust_threshold); detector(numpy_image<unsigned char>(img), rect_detections, adjust_threshold);
split_rect_detections(rect_detections, rectangles, split_rect_detections(rect_detections, rectangles,
detection_confidences, weight_indices); detection_confidences, weight_indices);
return rectangles; return rectangles;
} }
else else
{ {
pyramid_up(numpy_gray_image(img), temp, pyr); pyramid_up(numpy_image<unsigned char>(img), temp, pyr);
unsigned int levels = upsampling_amount-1; unsigned int levels = upsampling_amount-1;
while (levels > 0) while (levels > 0)
{ {
...@@ -79,19 +79,19 @@ namespace dlib ...@@ -79,19 +79,19 @@ namespace dlib
return rectangles; return rectangles;
} }
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
array2d<rgb_pixel> temp; array2d<rgb_pixel> temp;
if (upsampling_amount == 0) if (upsampling_amount == 0)
{ {
detector(numpy_rgb_image(img), rect_detections, adjust_threshold); detector(numpy_image<rgb_pixel>(img), rect_detections, adjust_threshold);
split_rect_detections(rect_detections, rectangles, split_rect_detections(rect_detections, rectangles,
detection_confidences, weight_indices); detection_confidences, weight_indices);
return rectangles; return rectangles;
} }
else else
{ {
pyramid_up(numpy_rgb_image(img), temp, pyr); pyramid_up(numpy_image<rgb_pixel>(img), temp, pyr);
unsigned int levels = upsampling_amount-1; unsigned int levels = upsampling_amount-1;
while (levels > 0) while (levels > 0)
{ {
...@@ -117,7 +117,7 @@ namespace dlib ...@@ -117,7 +117,7 @@ namespace dlib
inline std::vector<dlib::rectangle> run_detectors_with_upscale1 ( inline std::vector<dlib::rectangle> run_detectors_with_upscale1 (
std::vector<simple_object_detector >& detectors, std::vector<simple_object_detector >& detectors,
py::object img, py::array img,
const unsigned int upsampling_amount, const unsigned int upsampling_amount,
const double adjust_threshold, const double adjust_threshold,
std::vector<double>& detection_confidences, std::vector<double>& detection_confidences,
...@@ -129,19 +129,19 @@ namespace dlib ...@@ -129,19 +129,19 @@ namespace dlib
std::vector<rectangle> rectangles; std::vector<rectangle> rectangles;
std::vector<rect_detection> rect_detections; std::vector<rect_detection> rect_detections;
if (is_gray_python_image(img)) if (is_image<unsigned char>(img))
{ {
array2d<unsigned char> temp; array2d<unsigned char> temp;
if (upsampling_amount == 0) if (upsampling_amount == 0)
{ {
evaluate_detectors(detectors, numpy_gray_image(img), rect_detections, adjust_threshold); evaluate_detectors(detectors, numpy_image<unsigned char>(img), rect_detections, adjust_threshold);
split_rect_detections(rect_detections, rectangles, split_rect_detections(rect_detections, rectangles,
detection_confidences, weight_indices); detection_confidences, weight_indices);
return rectangles; return rectangles;
} }
else else
{ {
pyramid_up(numpy_gray_image(img), temp, pyr); pyramid_up(numpy_image<unsigned char>(img), temp, pyr);
unsigned int levels = upsampling_amount-1; unsigned int levels = upsampling_amount-1;
while (levels > 0) while (levels > 0)
{ {
...@@ -159,19 +159,19 @@ namespace dlib ...@@ -159,19 +159,19 @@ namespace dlib
return rectangles; return rectangles;
} }
} }
else if (is_rgb_python_image(img)) else if (is_image<rgb_pixel>(img))
{ {
array2d<rgb_pixel> temp; array2d<rgb_pixel> temp;
if (upsampling_amount == 0) if (upsampling_amount == 0)
{ {
evaluate_detectors(detectors, numpy_rgb_image(img), rect_detections, adjust_threshold); evaluate_detectors(detectors, numpy_image<rgb_pixel>(img), rect_detections, adjust_threshold);
split_rect_detections(rect_detections, rectangles, split_rect_detections(rect_detections, rectangles,
detection_confidences, weight_indices); detection_confidences, weight_indices);
return rectangles; return rectangles;
} }
else else
{ {
pyramid_up(numpy_rgb_image(img), temp, pyr); pyramid_up(numpy_image<rgb_pixel>(img), temp, pyr);
unsigned int levels = upsampling_amount-1; unsigned int levels = upsampling_amount-1;
while (levels > 0) while (levels > 0)
{ {
...@@ -197,7 +197,7 @@ namespace dlib ...@@ -197,7 +197,7 @@ namespace dlib
inline std::vector<dlib::rectangle> run_detector_with_upscale2 ( inline std::vector<dlib::rectangle> run_detector_with_upscale2 (
dlib::simple_object_detector& detector, dlib::simple_object_detector& detector,
py::object img, py::array img,
const unsigned int upsampling_amount const unsigned int upsampling_amount
) )
...@@ -213,7 +213,7 @@ namespace dlib ...@@ -213,7 +213,7 @@ namespace dlib
inline py::tuple run_rect_detector ( inline py::tuple run_rect_detector (
dlib::simple_object_detector& detector, dlib::simple_object_detector& detector,
py::object img, py::array img,
const unsigned int upsampling_amount, const unsigned int upsampling_amount,
const double adjust_threshold) const double adjust_threshold)
{ {
...@@ -234,7 +234,7 @@ namespace dlib ...@@ -234,7 +234,7 @@ namespace dlib
inline py::tuple run_multiple_rect_detectors ( inline py::tuple run_multiple_rect_detectors (
py::list& detectors, py::list& detectors,
py::object img, py::array img,
const unsigned int upsampling_amount, const unsigned int upsampling_amount,
const double adjust_threshold) const double adjust_threshold)
{ {
...@@ -272,13 +272,13 @@ namespace dlib ...@@ -272,13 +272,13 @@ namespace dlib
simple_object_detector_py(simple_object_detector& _detector, unsigned int _upsampling_amount) : simple_object_detector_py(simple_object_detector& _detector, unsigned int _upsampling_amount) :
detector(_detector), upsampling_amount(_upsampling_amount) {} detector(_detector), upsampling_amount(_upsampling_amount) {}
std::vector<dlib::rectangle> run_detector1 (py::object img, std::vector<dlib::rectangle> run_detector1 (py::array img,
const unsigned int upsampling_amount_) const unsigned int upsampling_amount_)
{ {
return run_detector_with_upscale2(detector, img, upsampling_amount_); return run_detector_with_upscale2(detector, img, upsampling_amount_);
} }
std::vector<dlib::rectangle> run_detector2 (py::object img) std::vector<dlib::rectangle> run_detector2 (py::array img)
{ {
return run_detector_with_upscale2(detector, img, upsampling_amount); return run_detector_with_upscale2(detector, img, upsampling_amount);
} }
......
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