// Copyright (C) 2017 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #include #include #include #include #include #include #include #include "indexing.h" #include #include using namespace dlib; using namespace std; using namespace boost::python; typedef matrix cv; class face_recognition_model_v1 { public: face_recognition_model_v1(const std::string& model_filename) { deserialize(model_filename) >> net; cropper = make_shared(); cropper->set_chip_dims(150,150); cropper->set_randomly_flip(true); cropper->set_max_object_size(0.99999); cropper->set_background_crops_fraction(0); cropper->set_min_object_size(0.97); cropper->set_translate_amount(0.02); cropper->set_max_rotation_degrees(3); } boost::python::list cluster(boost::python::list descriptors, float threshold) { boost::python::list clusters; size_t num_descriptors = len(descriptors); // In particular, one simple thing we can do is face clustering. This next bit of code // creates a graph of connected faces and then uses the Chinese whispers graph clustering // algorithm to identify how many people there are and which faces belong to whom. std::vector edges; std::vector labels; for (size_t i = 0; i < num_descriptors; ++i) { for (size_t j = i+1; j < num_descriptors; ++j) { // Faces are connected in the graph if they are close enough. Here we check if // the distance between two face descriptors is less than 0.6, which is the // decision threshold the network was trained to use. Although you can // certainly use any other threshold you find useful. matrix first_descriptor = boost::python::extract>(descriptors[i]); matrix second_descriptor = boost::python::extract>(descriptors[j]); if (length(first_descriptor-second_descriptor) < threshold) edges.push_back(sample_pair(i,j)); } } const auto num_clusters = chinese_whispers(edges, labels); for (size_t i = 0; i < labels.size(); ++i) { clusters.append(labels[i]); } return clusters; } void save_image_chip ( object img, const full_object_detection& face, const std::string& chip_filename ) { std::vector faces(1, face); save_image_chips(img, faces, chip_filename); return; } void save_image_chips ( object img, const std::vector& faces, const std::string& chip_filename ) { int num_faces = faces.size(); std::vector dets; for (auto& f : faces) dets.push_back(get_face_chip_details(f, 150, 0.25)); dlib::array> face_chips; extract_image_chips(numpy_rgb_image(img), dets, face_chips); int i=0; for (auto& chip : face_chips) { i++; if(num_faces > 1) { const std::string& file_name = chip_filename + "_" + std::to_string(i) + ".jpg"; save_jpeg(chip, file_name); } else { const std::string& file_name = chip_filename + ".jpg"; save_jpeg(chip, file_name); } } } matrix compute_face_descriptor ( object img, const full_object_detection& face, const int num_jitters ) { std::vector faces(1, face); return compute_face_descriptors(img, faces, num_jitters)[0]; } std::vector> compute_face_descriptors ( object img, const std::vector& faces, const int num_jitters ) { if (!is_rgb_python_image(img)) throw dlib::error("Unsupported image type, must be RGB image."); for (auto& f : faces) { if (f.num_parts() != 68) throw dlib::error("The full_object_detection must use the iBUG 300W 68 point face landmark style."); } std::vector dets; for (auto& f : faces) dets.push_back(get_face_chip_details(f, 150, 0.25)); dlib::array> face_chips; extract_image_chips(numpy_rgb_image(img), dets, face_chips); std::vector> face_descriptors; face_descriptors.reserve(face_chips.size()); if (num_jitters <= 1) { // extract descriptors and convert from float vectors to double vectors for (auto& d : net(face_chips,16)) face_descriptors.push_back(matrix_cast(d)); } else { for (auto& fimg : face_chips) face_descriptors.push_back(matrix_cast(mean(mat(net(jitter_image(fimg,num_jitters),16))))); } return face_descriptors; } private: std::shared_ptr cropper; std::vector> jitter_image( const matrix& img, const int num_jitters ) { std::vector raw_boxes(1), ignored_crop_boxes; raw_boxes[0] = shrink_rect(get_rect(img),3); std::vector> crops; matrix temp; for (int i = 0; i < num_jitters; ++i) { (*cropper)(img, raw_boxes, temp, ignored_crop_boxes); crops.push_back(move(temp)); } return crops; } template