@ -39,79 +39,6 @@ public:
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 < sample_pair > edges ;
std : : vector < unsigned long > 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 < 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 ] ) ;
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 < full_object_detection > faces ( 1 , face ) ;
save_image_chips ( img , faces , chip_filename ) ;
return ;
}
void save_image_chips (
object img ,
const std : : vector < full_object_detection > & faces ,
const std : : string & chip_filename
)
{
int num_faces = faces . size ( ) ;
std : : vector < chip_details > dets ;
for ( auto & f : faces )
dets . push_back ( get_face_chip_details ( f , 150 , 0.25 ) ) ;
dlib : : array < matrix < rgb_pixel > > 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 < double , 0 , 1 > compute_face_descriptor (
object img ,
const full_object_detection & face ,
@ -215,6 +142,78 @@ private:
anet_type net ;
} ;
// ----------------------------------------------------------------------------------------
boost : : python : : list chinese_whispers_clustering ( boost : : python : : list descriptors , float threshold )
{
boost : : python : : list clusters ;
size_t num_descriptors = 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
// objects belong to which cluster.
std : : vector < sample_pair > edges ;
std : : vector < unsigned long > labels ;
for ( size_t i = 0 ; i < num_descriptors ; + + i )
{
for ( size_t j = i + 1 ; 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 ] ) ;
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_face_chips (
object img ,
const std : : vector < full_object_detection > & faces ,
const std : : string & chip_filename
)
{
int num_faces = faces . size ( ) ;
std : : vector < chip_details > dets ;
for ( auto & f : faces )
dets . push_back ( get_face_chip_details ( f , 150 , 0.25 ) ) ;
dlib : : array < matrix < rgb_pixel > > 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 ) ;
}
}
}
void save_face_chip (
object img ,
const full_object_detection & face ,
const std : : string & chip_filename
)
{
std : : vector < full_object_detection > faces ( 1 , face ) ;
save_face_chips ( img , faces , chip_filename ) ;
return ;
}
// ----------------------------------------------------------------------------------------
@ -230,18 +229,19 @@ void bind_face_recognition()
. def ( " compute_face_descriptor " , & face_recognition_model_v1 : : compute_face_descriptors , ( arg ( " img " ) , arg ( " faces " ) , 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_image_chip " , & face_recognition_model_v1 : : save_image_chip , ( arg ( " img " ) , arg ( " face " ) , arg ( " chip_filename " ) ) ,
" 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 "
)
. def ( " save_image_chips " , & face_recognition_model_v1 : : save_image_chips , ( arg ( " img " ) , arg ( " faces " ) , arg ( " chip_filename " ) ) ,
" 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 "
)
. def ( " cluster " , & face_recognition_model_v1 : : cluster , ( arg ( " descriptors " ) , arg ( " threshold " ) ) ,
" Takes a list of descriptors and returns a list that contains a label for each descriptor. Clustering is done using chinese_whispers. "
) ;
}
def ( " save_face_chip " , & save_face_chip , ( arg ( " img " ) , arg ( " face " ) , arg ( " chip_filename " ) ) ,
" 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. "
) ;
def ( " save_face_chips " , & save_face_chips , ( arg ( " img " ) , arg ( " faces " ) , arg ( " chip_filename " ) ) ,
" 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. "
) ;
def ( " chinese_whispers_clustering " , & chinese_whispers_clustering , ( arg ( " descriptors " ) , 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. " )