2015-03-09 18:22:07 +00:00
# include <iostream>
# include <memory>
# include <chrono>
2016-02-03 12:20:08 +00:00
# include <fstream>
# include <boost/filesystem.hpp>
# include <boost/timer/timer.hpp>
# include <boost/program_options.hpp>
# include <boost/algorithm/string.hpp>
2015-03-09 18:22:07 +00:00
# include "Frame.h"
# include "Face.h"
# include "FrameDetector.h"
# include "AffdexException.h"
2016-02-03 12:20:08 +00:00
# include "AFaceListener.hpp"
# include "PlottingImageListener.hpp"
2017-11-17 15:17:31 +00:00
# include "LoggingImageListener.hpp"
2016-02-03 12:20:08 +00:00
# include "StatusListener.hpp"
2017-11-16 16:48:04 +00:00
2015-03-09 18:22:07 +00:00
using namespace std ;
2015-04-21 18:45:01 +00:00
using namespace affdex ;
2017-11-16 16:48:04 +00:00
using namespace cv ;
2015-03-09 18:22:07 +00:00
2017-11-12 22:23:09 +00:00
2017-11-16 19:12:29 +00:00
FeaturePoint minPoint ( VecFeaturePoint points )
{
VecFeaturePoint : : iterator it = points . begin ( ) ;
FeaturePoint ret = * it ;
for ( ; it ! = points . end ( ) ; it + + )
{
if ( it - > x < ret . x ) ret . x = it - > x ;
if ( it - > y < ret . y ) ret . y = it - > y ;
}
return ret ;
} ;
FeaturePoint maxPoint ( VecFeaturePoint points )
{
VecFeaturePoint : : iterator it = points . begin ( ) ;
FeaturePoint ret = * it ;
for ( ; it ! = points . end ( ) ; it + + )
{
if ( it - > x > ret . x ) ret . x = it - > x ;
if ( it - > y > ret . y ) ret . y = it - > y ;
}
return ret ;
} ;
2017-11-16 16:11:28 +00:00
std : : string getAsJson ( int framenr , const std : : map < FaceId , Face > faces , const double timeStamp )
2017-11-12 22:23:09 +00:00
{
std : : stringstream ss ;
2017-11-17 15:17:31 +00:00
ss < < " { " < < " \" t \" : " < < timeStamp < < " , " ;
ss < < " \" nr \" : " < < framenr < < " , " ;
ss < < " \" faces \" :[ " ;
2017-11-12 22:23:09 +00:00
int i ( 0 ) ;
2017-11-16 19:12:29 +00:00
2017-11-12 22:23:09 +00:00
for ( auto & face_id_pair : faces )
{
Face f = face_id_pair . second ;
2017-11-16 19:12:29 +00:00
2017-11-12 22:23:09 +00:00
if ( i > 0 ) { ss < < " , " ; }
i + + ;
2017-11-16 16:48:04 +00:00
2017-11-12 22:23:09 +00:00
ss < < " { " ;
// fStream << timeStamp << ","
// << f.id << ","
// << f.measurements.interocularDistance << ","
// << glassesMap[f.appearance.glasses] << ","
// << ageMap[f.appearance.age] << ","
// << ethnicityMap[f.appearance.ethnicity] << ","
// << genderMap[f.appearance.gender] << ","
// << affdex::EmojiToString(f.emojis.dominantEmoji) << ",";
float * values = ( float * ) & f . measurements . orientation ;
2017-11-12 22:33:12 +00:00
for ( std : : string angle : { " pitch " , " yaw " , " roll " } )
2017-11-12 22:23:09 +00:00
{
2017-11-17 15:17:31 +00:00
ss < < " \" " < < angle < < " \" : " < < ( * values ) < < " , " ;
2017-11-12 22:23:09 +00:00
values + + ;
}
values = ( float * ) & f . emotions ;
2017-11-12 22:33:12 +00:00
for ( std : : string emotion : {
" joy " , " fear " , " disgust " , " sadness " , " anger " ,
" surprise " , " contempt " , " valence " , " engagement "
} )
2017-11-12 22:23:09 +00:00
{
2017-11-17 15:17:31 +00:00
ss < < " \" " < < emotion < < " \" : " < < ( * values ) < < " , " ;
2017-11-12 22:23:09 +00:00
values + + ;
}
values = ( float * ) & f . expressions ;
2017-11-12 22:34:03 +00:00
for ( std : : string expression : {
2017-11-12 22:33:12 +00:00
" smile " , " innerBrowRaise " , " browRaise " , " browFurrow " , " noseWrinkle " ,
" upperLipRaise " , " lipCornerDepressor " , " chinRaise " , " lipPucker " , " lipPress " ,
" lipSuck " , " mouthOpen " , " smirk " , " eyeClosure " , " attention " , " eyeWiden " , " cheekRaise " ,
" lidTighten " , " dimpler " , " lipStretch " , " jawDrop "
} )
2017-11-12 22:23:09 +00:00
{
2017-11-17 15:17:31 +00:00
ss < < " \" " < < expression < < " \" : " < < ( * values ) < < " , " ;
2017-11-12 22:23:09 +00:00
values + + ;
}
2017-11-16 19:12:29 +00:00
FeaturePoint tl = minPoint ( f . featurePoints ) ;
FeaturePoint br = maxPoint ( f . featurePoints ) ;
2017-11-17 15:17:31 +00:00
ss < < " \" rect \" :{ \" x \" : " < < tl . x < < " , \" y \" : " < < tl . y
< < " , \" w \" : " < < ( br . x - tl . x ) < < " , \" h \" : " < < ( br . y - tl . y ) < < " }, " ;
2017-11-16 19:12:29 +00:00
2017-11-17 15:17:31 +00:00
ss < < " \" ioDistance \" : " < < f . measurements . interocularDistance < < " , " ;
ss < < " \" id \" : " < < f . id ;
2017-11-12 22:23:09 +00:00
ss < < " } " ;
}
ss < < " ] " ; // faces
ss < < " } " ;
return ss . str ( ) ;
}
2017-11-12 22:15:44 +00:00
/// <summary>
/// Project for demoing the Windows SDK CameraDetector class (grabbing and processing frames from the camera).
/// </summary>
2016-02-03 12:20:08 +00:00
int main ( int argsc , char * * argsv )
2015-03-09 18:22:07 +00:00
{
2016-02-03 12:20:08 +00:00
namespace po = boost : : program_options ; // abbreviate namespace
std : : cerr < < " Hit ESCAPE key to exit app.. " < < endl ;
shared_ptr < FrameDetector > frameDetector ;
try {
const std : : vector < int > DEFAULT_RESOLUTION { 640 , 480 } ;
affdex : : path DATA_FOLDER ;
std : : vector < int > resolution ;
int process_framerate = 30 ;
int buffer_length = 2 ;
unsigned int nFaces = 1 ;
bool draw_display = true ;
2017-11-17 15:17:31 +00:00
int faceDetectorMode = ( int ) FaceDetectorMode : : SMALL_FACES ;
boost : : filesystem : : path imgPath ( " ~/emo_in_file.jpg " ) ;
boost : : filesystem : : path outPath ( " ~/output/ " ) ;
2016-02-03 12:20:08 +00:00
float last_timestamp = - 1.0f ;
float capture_fps = - 1.0f ;
const int precision = 2 ;
std : : cerr . precision ( precision ) ;
std : : cout . precision ( precision ) ;
2016-04-08 14:12:02 +00:00
po : : options_description description ( " Project for demoing the Affdex SDK CameraDetector class (grabbing and processing frames from the camera). " ) ;
2016-02-03 12:20:08 +00:00
description . add_options ( )
( " help,h " , po : : bool_switch ( ) - > default_value ( false ) , " Display this help message. " )
# ifdef _WIN32
( " data,d " , po : : wvalue < affdex : : path > ( & DATA_FOLDER ) - > default_value ( affdex : : path ( L " data " ) , std : : string ( " data " ) ) , " Path to the data folder " )
# else // _WIN32
( " data,d " , po : : value < affdex : : path > ( & DATA_FOLDER ) - > default_value ( affdex : : path ( " data " ) , std : : string ( " data " ) ) , " Path to the data folder " )
# endif // _WIN32
( " pfps " , po : : value < int > ( & process_framerate ) - > default_value ( 30 ) , " Processing framerate. " )
( " bufferLen " , po : : value < int > ( & buffer_length ) - > default_value ( 30 ) , " process buffer size. " )
2017-11-17 15:17:31 +00:00
( " faceMode " , po : : value < int > ( & faceDetectorMode ) - > default_value ( ( int ) FaceDetectorMode : : SMALL_FACES ) , " Face detector mode (large faces vs small faces). " )
2016-02-03 12:20:08 +00:00
( " numFaces " , po : : value < unsigned int > ( & nFaces ) - > default_value ( 1 ) , " Number of faces to be tracked. " )
( " draw " , po : : value < bool > ( & draw_display ) - > default_value ( true ) , " Draw metrics on screen. " )
2017-11-17 15:17:31 +00:00
//~ ("file,f", po::value< boost::filesystem::path >(&imgPath)->default_value(imgPath), "Filename of image that is watched/tracked for changes.")
( " frameOutput,o " , po : : value < boost : : filesystem : : path > ( & outPath ) - > default_value ( outPath ) , " Directory to store the frame in (and json) " )
2016-02-03 12:20:08 +00:00
;
po : : variables_map args ;
try
{
po : : store ( po : : command_line_parser ( argsc , argsv ) . options ( description ) . run ( ) , args ) ;
if ( args [ " help " ] . as < bool > ( ) )
{
std : : cout < < description < < std : : endl ;
return 0 ;
}
po : : notify ( args ) ;
}
catch ( po : : error & e )
{
std : : cerr < < " ERROR: " < < e . what ( ) < < std : : endl < < std : : endl ;
std : : cerr < < " For help, use the -h option. " < < std : : endl < < std : : endl ;
return 1 ;
}
if ( ! boost : : filesystem : : exists ( DATA_FOLDER ) )
{
std : : cerr < < " Folder doesn't exist: " < < std : : string ( DATA_FOLDER . begin ( ) , DATA_FOLDER . end ( ) ) < < std : : endl < < std : : endl ; ;
std : : cerr < < " Try specifying the folder through the command line " < < std : : endl ;
std : : cerr < < description < < std : : endl ;
return 1 ;
}
2017-11-17 15:17:31 +00:00
if ( ! boost : : filesystem : : exists ( outPath ) )
2016-02-03 12:20:08 +00:00
{
2017-11-17 15:17:31 +00:00
std : : cerr < < " Folder doesn't exist: " < < outPath . native ( ) < < std : : endl < < std : : endl ; ;
std : : cerr < < " Try specifying the output folder through the command line " < < std : : endl ;
std : : cerr < < description < < std : : endl ;
2016-02-03 12:20:08 +00:00
return 1 ;
}
std : : ofstream csvFileStream ;
std : : cerr < < " Initializing Affdex FrameDetector " < < endl ;
shared_ptr < FaceListener > faceListenPtr ( new AFaceListener ( ) ) ;
shared_ptr < PlottingImageListener > listenPtr ( new PlottingImageListener ( csvFileStream , draw_display ) ) ; // Instanciate the ImageListener class
shared_ptr < StatusListener > videoListenPtr ( new StatusListener ( ) ) ;
frameDetector = make_shared < FrameDetector > ( buffer_length , process_framerate , nFaces , ( affdex : : FaceDetectorMode ) faceDetectorMode ) ; // Init the FrameDetector Class
//Initialize detectors
frameDetector - > setDetectAllEmotions ( true ) ;
frameDetector - > setDetectAllExpressions ( true ) ;
2017-11-12 22:15:44 +00:00
frameDetector - > setDetectAllEmojis ( false ) ;
frameDetector - > setDetectAllAppearances ( false ) ;
2016-02-03 12:20:08 +00:00
frameDetector - > setClassifierPath ( DATA_FOLDER ) ;
frameDetector - > setImageListener ( listenPtr . get ( ) ) ;
frameDetector - > setFaceListener ( faceListenPtr . get ( ) ) ;
frameDetector - > setProcessStatusListener ( videoListenPtr . get ( ) ) ;
2017-11-16 16:11:28 +00:00
2016-02-03 12:20:08 +00:00
auto start_time = std : : chrono : : system_clock : : now ( ) ;
2017-11-17 15:17:31 +00:00
2016-02-03 12:20:08 +00:00
std : : cout < < " Max num of faces set to: " < < frameDetector - > getMaxNumberFaces ( ) < < std : : endl ;
std : : string mode ;
switch ( frameDetector - > getFaceDetectorMode ( ) )
{
case FaceDetectorMode : : LARGE_FACES :
mode = " LARGE_FACES " ;
break ;
case FaceDetectorMode : : SMALL_FACES :
mode = " SMALL_FACES " ;
break ;
default :
break ;
}
std : : cout < < " Face detector mode set to: " < < mode < < std : : endl ;
//Start the frame detector thread.
frameDetector - > start ( ) ;
2017-11-17 15:17:31 +00:00
int frameNrIn = 1 ;
int frameNrOut = 1 ;
std : : time_t lastImgUpdate ( 0 ) ;
while ( true ) { //(cv::waitKey(20) != -1);
char buff [ 100 ] ;
snprintf ( buff , sizeof ( buff ) , " frame%06d.jpg " , frameNrIn ) ;
boost : : filesystem : : path imgPath = outPath / buff ;
if ( ! boost : : filesystem : : exists ( imgPath . native ( ) ) | | frameNrIn > frameNrOut ) {
// wait for file to appear
// and for the in file to be parsed (frame out)
usleep ( 5000 ) ; // wait 1/20 sec to avoid useless fast loop
} else {
std : : cerr < < " Read " < < imgPath . native ( ) < < std : : endl ;
char buff [ 100 ] ;
snprintf ( buff , sizeof ( buff ) , " frame%06d.json " , frameNrIn ) ;
boost : : filesystem : : path jsonPath = outPath / buff ;
// don't redo existing jsons
if ( ! boost : : filesystem : : exists ( jsonPath . native ( ) ) ) {
cv : : Mat img = imread ( imgPath . native ( ) , 1 ) ;
//Calculate the Image timestamp and the capture frame rate;
const auto milliseconds = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) - start_time ) ;
const double seconds = milliseconds . count ( ) / 1000.f ;
// Create a frame
Frame f ( img . size ( ) . width , img . size ( ) . height , img . data , Frame : : COLOR_FORMAT : : BGR , seconds ) ;
capture_fps = 1.0f / ( seconds - last_timestamp ) ;
last_timestamp = seconds ;
frameDetector - > process ( f ) ; //Pass the frame to detector
} else {
frameNrOut + + ; // this won't happen later, but nr. should stay equal if skipping items.
}
2017-11-16 19:12:29 +00:00
2017-11-17 15:17:31 +00:00
frameNrIn + + ;
}
// For each frame processed (returns async)
2016-02-03 12:20:08 +00:00
if ( listenPtr - > getDataSize ( ) > 0 )
{
2017-11-16 16:11:28 +00:00
2016-02-03 12:20:08 +00:00
std : : pair < Frame , std : : map < FaceId , Face > > dataPoint = listenPtr - > getData ( ) ;
Frame frame = dataPoint . first ;
std : : map < FaceId , Face > faces = dataPoint . second ;
// Draw metrics to the GUI
if ( draw_display )
{
listenPtr - > draw ( faces , frame ) ;
}
2017-11-17 15:17:31 +00:00
std : : string json = getAsJson ( frameNrOut , faces , frame . getTimestamp ( ) ) ;
std : : cout < < json < < std : : endl ;
2017-11-16 16:11:28 +00:00
2017-11-17 15:17:31 +00:00
// store json
char buff [ 100 ] ;
snprintf ( buff , sizeof ( buff ) , " frame%06d.json " , frameNrOut ) ;
boost : : filesystem : : path targetFilename = outPath / buff ;
std : : ofstream out ( targetFilename . native ( ) ) ;
std : : cerr < < " write " < < targetFilename . native ( ) < < std : : endl ;
out < < json < < " \n " ;
out . close ( ) ;
frameNrOut + + ;
2016-02-03 12:20:08 +00:00
}
}
std : : cerr < < " Stopping FrameDetector Thread " < < endl ;
frameDetector - > stop ( ) ; //Stop frame detector thread
}
catch ( AffdexException ex )
{
std : : cerr < < " Encountered an AffdexException " < < ex . what ( ) ;
return 1 ;
}
catch ( std : : runtime_error err )
{
std : : cerr < < " Encountered a runtime error " < < err . what ( ) ;
return 1 ;
}
catch ( std : : exception ex )
{
std : : cerr < < " Encountered an exception " < < ex . what ( ) ;
return 1 ;
}
catch ( . . . )
{
std : : cerr < < " Encountered an unhandled exception " ;
return 1 ;
}
return 0 ;
}