diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..07e56a4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +dist: trusty +language: cpp +compiler: + - gcc-4.8 +before_script: + - sudo apt-get install -y gcc-4.8 g++-4.8 libopencv-dev libboost1.55-all-dev cmake + - wget http://affdex-sdk-dist.s3-website-us-east-1.amazonaws.com/linux/download_sdk.html -O /tmp/affdex-sdk.tar.gz + - mkdir /tmp/affdex-sdk + - tar -xzvf /tmp/affdex-sdk.tar.gz -C /tmp/affdex-sdk + - mkdir build + - cd build + - cmake -DBOOST_ROOT=/usr/ -DOpenCV_DIR=/usr/ -DAFFDEX_DIR=/tmp/affdex-sdk .. +script: + - make diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..179a120 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,162 @@ +cmake_minimum_required(VERSION 2.6) + +set(rootProject cpp-sdk-samples) +project(${rootProject}) + +# CMake includes +include(cmake_modules/Macros.cmake) # Some custom macros we have writtens + +# ------------------- +# CMAKE - ENVIRONMENT +# -------------------- +set(CXX_COMPILER_WARNINGS "-Wreturn-type" CACHE STRING "Compiler warnings to use") +set(CMAKE_VERBOSE ON CACHE BOOL "Verbose mode") +# Setup "Profile" build type +set(CMAKE_CXX_FLAGS_PROFILE "-O3 -pg") +set(CMAKE_C_FLAGS_PROFILE "-O3 -pg") +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "-pg") +set(CMAKE_MODULE_LINKER_FLAGS_PROFILE "-pg") + +# Setup additional compiler warnings +status("Setting up compiler warnings") +if( MSVC ) + # Force to always compile with W4 + if( CMAKE_CXX_FLAGS MATCHES "/W[0-4]" ) + string( REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4" ) + endif() +elseif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) + # Update if necessary + set( CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS} ${CXX_COMPILER_WARNINGS}" ) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + status("Updating compiler to make use of C++14") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(bitness 64) # We have a 64-bit machine +else() + set(bitness 32) # We have a 32-bit machine +endif() +status("Bitness detected: ${bitness}") + + +# Setup install locations +if( NOT RUNTIME_INSTALL_DIRECTORY ) + set( RUNTIME_INSTALL_DIRECTORY "bin" CACHE STRING "Install sub-directory of CMAKE_INSTALL_PREFIX for RUNTIME targets (binaries, and *.dll on windows)." ) +endif( NOT RUNTIME_INSTALL_DIRECTORY ) + +# -------------------- +# LOCATE DEPENDENCIES +# -------------------- + +# OpenCV +# ---------------------------------------------------------------------------- +# find_package OpenCV to get OpenCV_FOUND, OpenCV_INCLUDE_DIRS, OpenCV_LIBS, OpenCV_LINK_LIBRARIES +# ---------------------------------------------------------------------------- +set( OPENCV_COMPONENTS ml highgui core imgproc objdetect ) +if( DEFINED OpenCV_DIR ) # Force the user to tell us which OpenCV they want (otherwise find_package can find the wrong one, cache it and changes to OpenCV_DIR are ignored) + find_package(OpenCV REQUIRED PATHS ${OpenCV_DIR}) + if( NOT OpenCV_FOUND) + message(SEND_ERROR "Failed to find OpenCV. Double check that \"OpenCV_DIR\" to the root build directory of OpenCV.") + endif(NOT OpenCV_FOUND) +else( DEFINED OpenCV_DIR ) + set( OpenCV_DIR "" CACHE PATH "Root directory for opencv BUILD directory." ) + message(FATAL_ERROR "\"OpenCV_DIR\" not set. Please explicitly provide the path to the root build directory of OpenCV.") +endif( DEFINED OpenCV_DIR ) + +# Boost package +# ---------------------------------------------------------------------------- +# BOOST_ROOT is needed by BoostConfig.cmake configuration file to +# look for the Boost includes / libraries: +# Boost_FOUND, Boost_INCLUDE_DIRS, Boost_LIBRARY_DIRS, Boost_LIBRARIES,Boost_VERSION +set(Boost_USE_MULTITHREADED ON) +set( BOOST_COMPONENTS system filesystem date_time regex thread timer chrono serialization log log_setup program_options) + +set( BOOST_MIN_VERSION "1.54.0" CACHE STRING "Minimum version of boost you would like to link against (e.g. C:/BOOST_1_55_0 is 1.55.0" ) +status("") +if( ANDROID ) + find_host_package( Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS ${BOOST_COMPONENTS} ) +else( ANDROID ) + find_package( Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS ${BOOST_COMPONENTS} ) +endif() +if( NOT Boost_FOUND ) + if( NOT DEFINED BOOST_ROOT ) + set( BOOST_ROOT "" CACHE PATH "Root directory for Boost." ) + endif( NOT DEFINED BOOST_ROOT ) + message( FATAL_ERROR "Failed to find Boost (or missing components). Double check that \"BOOST_ROOT\" is properly set") +endif( NOT Boost_FOUND ) + + + +# Affdex package +# ---------------------------------------------------------------------------- + +set (AFFDEX_FOUND FALSE) + +if( DEFINED AFFDEX_DIR ) + find_path(AFFDEX_INCLUDE_DIR FrameDetector.h + HINTS "${AFFDEX_DIR}/include" ) + + find_library(AFFDEX_LIBRARY NAMES affdex-native + HINTS "${AFFDEX_DIR}/lib" ) + + set(AFFDEX_INCLUDE_DIRS "${AFFDEX_INCLUDE_DIR}") + set(AFFDEX_LIBRARIES "${AFFDEX_LIBRARY}") + + if (AFFDEX_INCLUDE_DIR AND AFFDEX_LIBRARY) + set(AFFDEX_FOUND TRUE) + endif (AFFDEX_INCLUDE_DIR AND AFFDEX_LIBRARY) + + set(AFFDEX_DATA_DIR "${AFFDEX_DIR}/data") + + + if (NOT AFFDEX_FOUND) + message(FATAL_ERROR "Unable to find the Affdex found") + endif (NOT AFFDEX_FOUND) + +else (DEFINED AFFDEX_DIR) + message(FATAL_ERROR "Please define AFFDEX_DIR") +endif (DEFINED AFFDEX_DIR) + + +add_subdirectory(opencv-webcam-demo) +add_subdirectory(video-demo) + +# -------------------- +# SUMMARY +# -------------------- +status("${CMAKE_INCLUDE_DIRECTORIES}") +status( "------- SUMMARY ------- " ) +status( "Boost version found = ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION} (${Boost_VERSION})" ) +foreach( comp ${BOOST_COMPONENTS} ) + string( TOUPPER ${comp} COMP ) + status( " - ${comp}" 1 THEN "${Boost_${COMP}_LIBRARY}" ) +endforeach( comp ) + +status("") +status("Affdex") +foreach( lib ${AFFDEX_LIBRARIES} ) + status( "${lib}") +endforeach( lib ) + +status("") +status( "OpenCV version found = ${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH} (${OpenCV_VERSION})" ) +status( "OpenCV_LIB_DIR = ${OpenCV_DIR}/lib" ) +foreach( lib ${OpenCV_LIBRARIES} ) + foreach( comp ${OPENCV_COMPONENTS} ) + if( ${lib} MATCHES ${comp} ) + status( " - ${comp}" 1 THEN "${lib}" ) + endif( ${lib} MATCHES ${comp} ) + endforeach( comp ) +endforeach( lib ) + +status("") +status( "Apps identified for building:" ) +foreach( app ${${rootProject}_APPS} ) + status( " - ${app}" ) +endforeach( app ${${rootProject}_APPS} ) diff --git a/README.md b/README.md index 19f2d96..b71d2e0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -#Sample Apps for Affdex SDK for Windows +#Sample Apps for Affdex SDK for Windows and Linux -Welcome to our repository on GitHub! Here you will find example code to get you started with our Affdex SDK 3.0 for Windows and begin emotion-enabling you own app! Documentation for the Windows SDK is at Affectiva's Developer Portal. +Welcome to our repository on GitHub! Here you will find example code to get you started with our Affdex SDK 3.0 and begin emotion-enabling you own app! Documentation for the SDKs is available on the Affectiva's Developer Portal. [![Build status](https://ci.appveyor.com/api/projects/status/pn2y9h8a3nnkiw41?svg=true)] (https://ci.appveyor.com/project/ahamino/win-sdk-samples) @@ -8,22 +8,47 @@ Welcome to our repository on GitHub! Here you will find example code to get you Dependencies ------------ +*Windows* - Affdex SDK 3.0 (32 bit) - Visual Studio 2013 or higher +*Linux* +- Ubuntu 14.04 or higher or CentOS 7 or higher +- Affdex SDK 3.0 +- CMake 2.8 or higher +- GCC 4.8 or higher -*Additional dependencies for the C++ projects* +*Additional dependencies* - OpenCV 3.1 - Boost 1.59 Installation ------------ -- Download Affdex SDK for windows [from here](http://developer.affectiva.com/downloads) + +- Download Affdex SDK [from here](http://developer.affectiva.com/downloads) - Sign up for an evaluation license [by submitting this form](http://www.affectiva.com/45-day-free-trial/) + +*Windows* - Install the SDK using MSI installer. - The additional dependencies get installed automatically by NuGet. +*Ubuntu* + +```bashrc +sudo apt-get install build-essential libopencv-dev libboost1.55-all-dev cmake +wget http://developer.affectiva.com/downloads/linux +mkdir $HOME/affdex-sdk +tar -xzvf affdex-cpp-sdk-3.0-linux-64bit.tar.gz -C $HOME/affdex-sdk +export AFFDEX_DATA_DIR=$HOME/affdex-sdk/data +git clone https://github.com/Affectiva/cpp-sdk-samples.git $HOME/sdk-samples +mkdir $HOME/build +cd $HOME/build +cmake -DOpenCV_DIR=/usr/ -DBOOST_ROOT=/usr/ -DAFFDEX_DIR=$HOME/affdex-sdk $HOME/sdk-samples +make +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/affdex-sdk/lib +``` + OpenCV-webcam-demo (c++) ------------------ @@ -49,7 +74,7 @@ The following command line arguments can be used to run it: Video-demo (c++) ---------- -Project for demoing the Windows SDK [VideoDetector class](http://developer.affectiva.com/v3/windows/analyze-video/). It processs video files, displays the emotion metrics and exports the results in a csv file. +Project for demoing the C++ SDK [VideoDetector class](http://developer.affectiva.com/v3/windows/analyze-video/). It processs video files, displays the emotion metrics and exports the results in a csv file. The following command line arguments can be used to run it: diff --git a/cmake_modules/Macros.cmake b/cmake_modules/Macros.cmake new file mode 100644 index 0000000..eaef629 --- /dev/null +++ b/cmake_modules/Macros.cmake @@ -0,0 +1,106 @@ +# Extracting the subdirectories from a given folder +# +# Usage: +# SUBDIRLIST( SUBDIRS "path/to/base/dir" ) +# +# Source: http://stackoverflow.com/questions/7787823/cmake-how-to-get-the-name-of-all-subdirectories-of-a-directory + +MACRO(SUBDIRLIST result curdir) + FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) + SET(dirlist "") + FOREACH(child ${children}) + IF(IS_DIRECTORY ${curdir}/${child}) + SET(dirlist ${dirlist} ${child}) + ENDIF() + ENDFOREACH() + SET(${result} ${dirlist}) +ENDMACRO() + +# Search packages for host system instead of packages for target system +# in case of cross compilation thess macro should be defined by toolchain file +# adopted from OpenCV +if(NOT COMMAND find_host_package) + macro(find_host_package) + find_package(${ARGN}) + endmacro() +endif() +if(NOT COMMAND find_host_program) + macro(find_host_program) + find_program(${ARGN}) + endmacro() +endif() + +macro(check_environment_variables) + foreach(_var ${ARGN}) + if(NOT DEFINED ${_var} AND DEFINED ENV{${_var}}) + set(__value "$ENV{${_var}}") + file(TO_CMAKE_PATH "${__value}" __value) # Assume that we receive paths + set(${_var} "${__value}") + message(STATUS "Update variable ${_var} from environment: ${${_var}}") + endif() + endforeach() +endmacro() + +# Status convinience function. +# adopted from OpenCV +function(output_status msg) + message(STATUS "${msg}") + string(REPLACE "\\" "\\\\" msg "${msg}") + string(REPLACE "\"" "\\\"" msg "${msg}") +endfunction() + +# Status report function. +# Automatically align right column and selects text based on condition. +# Usage: +# status() +# status( [ ...]) +# status( THEN ELSE ) +# adopted from OpenCV +function(status text) + set(status_cond) + set(status_then) + set(status_else) + + set(status_current_name "cond") + foreach(arg ${ARGN}) + if(arg STREQUAL "THEN") + set(status_current_name "then") + elseif(arg STREQUAL "ELSE") + set(status_current_name "else") + else() + list(APPEND status_${status_current_name} ${arg}) + endif() + endforeach() + + if(DEFINED status_cond) + set(status_placeholder_length 18) + string(RANDOM LENGTH ${status_placeholder_length} ALPHABET " " status_placeholder) + string(LENGTH "${text}" status_text_length) + if(status_text_length LESS status_placeholder_length) + string(SUBSTRING "${text}${status_placeholder}" 0 ${status_placeholder_length} status_text) + elseif(DEFINED status_then OR DEFINED status_else) + output_status("${text}") + set(status_text "${status_placeholder}") + else() + set(status_text "${text}") + endif() + + if(DEFINED status_then OR DEFINED status_else) + if(${status_cond}) + string(REPLACE ";" " " status_then "${status_then}") + string(REGEX REPLACE "^[ \t]+" "" status_then "${status_then}") + output_status("${status_text} ${status_then}") + else() + string(REPLACE ";" " " status_else "${status_else}") + string(REGEX REPLACE "^[ \t]+" "" status_else "${status_else}") + output_status("${status_text} ${status_else}") + endif() + else() + string(REPLACE ";" " " status_cond "${status_cond}") + string(REGEX REPLACE "^[ \t]+" "" status_cond "${status_cond}") + output_status("${status_text} ${status_cond}") + endif() + else() + output_status("${text}") + endif() +endfunction() diff --git a/common/PlottingImageListener.hpp b/common/PlottingImageListener.hpp index cbf3638..02d2eec 100644 --- a/common/PlottingImageListener.hpp +++ b/common/PlottingImageListener.hpp @@ -24,10 +24,10 @@ using namespace affdex; class PlottingImageListener : public ImageListener { - + std::mutex mMutex; std::deque > > mDataArray; - + double mCaptureLastTS; double mCaptureFPS; double mProcessLastTS; @@ -38,17 +38,18 @@ class PlottingImageListener : public ImageListener const int spacing = 10; const float font_size = 0.5f; const int font = cv::FONT_HERSHEY_COMPLEX_SMALL; - + std::vector expressions; std::vector emotions; + std::vector emojis; std::vector headAngles; - + std::map glassesMap; std::map genderMap; - + public: - - + + PlottingImageListener(std::ofstream &csv, const bool draw_display) : fStream(csv), mDrawDisplay(draw_display), mStartT(std::chrono::system_clock::now()), mCaptureLastTS(-1.0f), mCaptureFPS(-1.0f), @@ -59,35 +60,45 @@ public: "upperLipRaise", "lipCornerDepressor", "chinRaise", "lipPucker", "lipPress", "lipSuck", "mouthOpen", "smirk", "eyeClosure", "attention" }; - + emotions = { "joy", "fear", "disgust", "sadness", "anger", "surprise", "contempt", "valence", "engagement" }; - + headAngles = { "pitch", "yaw", "roll" }; - + + + emojis = std::vector { + "relaxed", "smiley", "laughing", + "kissing", "disappointed", + "rage", "smirk", "wink", + "stuckOutTongueWinkingEye", "stuckOutTongue", + "flushed", "scream" + }; + genderMap = std::map { { affdex::Gender::Male, "male" }, { affdex::Gender::Female, "female" }, { affdex::Gender::Unknown, "unknown" }, - + }; - + glassesMap = std::map { { affdex::Glasses::Yes, "glasses" }, { affdex::Glasses::No, "no glasses" } }; - - fStream << "TimeStamp,faceId,interocularDistance,glasses,gender,"; + + fStream << "TimeStamp,faceId,interocularDistance,glasses,gender,dominantEmoji,"; for (std::string angle : headAngles) fStream << angle << ","; for (std::string emotion : emotions) fStream << emotion << ","; for (std::string expression : expressions) fStream << expression << ","; + for (std::string emoji : emojis) fStream << emoji << ","; fStream << std::endl; fStream.precision(4); fStream << std::fixed; } - + FeaturePoint minPoint(VecFeaturePoint points) { VecFeaturePoint::iterator it = points.begin(); @@ -99,7 +110,7 @@ public: } return ret; }; - + FeaturePoint maxPoint( VecFeaturePoint points) { VecFeaturePoint::iterator it = points.begin(); @@ -111,27 +122,27 @@ public: } return ret; }; - - + + double getProcessingFrameRate() { std::lock_guard lg(mMutex); return mProcessFPS; } - + double getCaptureFrameRate() { std::lock_guard lg(mMutex); return mCaptureFPS; } - + int getDataSize() { std::lock_guard lg(mMutex); return mDataArray.size(); - + } - + std::pair> getData() { std::lock_guard lg(mMutex); @@ -139,7 +150,7 @@ public: mDataArray.pop_front(); return dpoint; } - + void onImageResults(std::map faces, Frame image) override { std::lock_guard lg(mMutex); @@ -150,58 +161,68 @@ public: mProcessFPS = 1.0f / (seconds - mProcessLastTS); mProcessLastTS = seconds; }; - + void onImageCapture(Frame image) override { std::lock_guard lg(mMutex); mCaptureFPS = 1.0f / (image.getTimestamp() - mCaptureLastTS); mCaptureLastTS = image.getTimestamp(); }; - + void outputToFile(const std::map faces, const double timeStamp) { if (faces.empty()) { - fStream << timeStamp << "nan,nan,no glasses,unknown,"; + fStream << timeStamp << "nan,nan,no glasses,unknown, unknown,"; for (std::string angle : headAngles) fStream << "nan,"; for (std::string emotion : emotions) fStream << "nan,"; for (std::string expression : expressions) fStream << "nan,"; + for (std::string emoji : emojis) fStream << "nan,"; fStream << std::endl; } for (auto & face_id_pair : faces) { Face f = face_id_pair.second; - + fStream << timeStamp << "," << f.id << "," << f.measurements.interocularDistance << "," << glassesMap[f.appearance.glasses] << "," - << genderMap[f.appearance.gender] << ","; - + << genderMap[f.appearance.gender] << "," + << affdex::EmojiToString(f.emojis.dominantEmoji) << ","; + float *values = (float *)&f.measurements.orientation; for (std::string angle : headAngles) { fStream << (*values) << ","; values++; } - + values = (float *)&f.emotions; for (std::string emotion : emotions) { fStream << (*values) << ","; values++; } - + values = (float *)&f.expressions; for (std::string expression : expressions) { fStream << (*values) << ","; values++; } + + values = (float *)&f.emojis; + for (std::string emoji : emojis) + { + fStream << (*values) << ","; + values++; + } + fStream << std::endl; } } - + void drawValues(const float * first, const std::vector names, const int x, int &padding, const cv::Scalar clr, cv::Mat img) @@ -217,18 +238,18 @@ public: first++; } } - + void draw(const std::map faces, Frame image) { std::shared_ptr imgdata = image.getBGRByteArray(); cv::Mat img = cv::Mat(image.getHeight(), image.getWidth(), CV_8UC3, imgdata.get()); - + const int left_margin = 30; - - - cv::Scalar clr = cv::Scalar(255, 255, 255); + + + cv::Scalar clr = cv::Scalar(0, 0, 255); cv::Scalar header_clr = cv::Scalar(255, 0, 0); - + for (auto & face_id_pair : faces) { Face f = face_id_pair.second; @@ -239,49 +260,56 @@ public: } FeaturePoint tl = minPoint(points); FeaturePoint br = maxPoint(points); - + //Output the results of the different classifiers. int padding = tl.y + 10; - + cv::putText(img, "APPEARANCE", cv::Point(br.x, padding += (spacing * 2)), font, font_size, header_clr); cv::putText(img, genderMap[f.appearance.gender], cv::Point(br.x, padding += spacing), font, font_size, clr); cv::putText(img, glassesMap[f.appearance.glasses], cv::Point(br.x, padding += spacing), font, font_size, clr); - - - + + + Orientation headAngles = f.measurements.orientation; - + char strAngles[100]; sprintf(strAngles, "Pitch: %3.2f Yaw: %3.2f Roll: %3.2f Interocular: %3.2f", headAngles.pitch, headAngles.yaw, headAngles.roll, f.measurements.interocularDistance); - - - + + + char fId[10]; sprintf(fId, "ID: %i", f.id); cv::putText(img, fId, cv::Point(br.x, padding += spacing), font, font_size, clr); - + cv::putText(img, "MEASUREMENTS", cv::Point(br.x, padding += (spacing * 2)), font, font_size, header_clr); - + cv::putText(img, strAngles, cv::Point(br.x, padding += spacing), font, font_size, clr); - + + cv::putText(img, "EMOJIS", cv::Point(br.x, padding += (spacing * 2)), font, font_size, header_clr); + + cv::putText(img, "dominantEmoji: " + affdex::EmojiToString(f.emojis.dominantEmoji), + cv::Point(br.x, padding += spacing), font, font_size, clr); + + drawValues((float *)&f.emojis, emojis, br.x, padding, clr, img); + cv::putText(img, "EXPRESSIONS", cv::Point(br.x, padding += (spacing * 2)), font, font_size, header_clr); - + drawValues((float *)&f.expressions, expressions, br.x, padding, clr, img); - + cv::putText(img, "EMOTIONS", cv::Point(br.x, padding += (spacing * 2)), font, font_size, header_clr); - + drawValues((float *)&f.emotions, emotions, br.x, padding, clr, img); - + } char fps_str[50]; sprintf(fps_str, "capture fps: %2.0f", mCaptureFPS); cv::putText(img, fps_str, cv::Point(img.cols - 110, img.rows - left_margin - spacing), font, font_size, clr); sprintf(fps_str, "process fps: %2.0f", mProcessFPS); cv::putText(img, fps_str, cv::Point(img.cols - 110, img.rows - left_margin), font, font_size, clr); - + cv::imshow("analyze video", img); cv::waitKey(5); } - + }; diff --git a/opencv-webcam-demo/CMakeLists.txt b/opencv-webcam-demo/CMakeLists.txt new file mode 100755 index 0000000..ab707c3 --- /dev/null +++ b/opencv-webcam-demo/CMakeLists.txt @@ -0,0 +1,34 @@ +# -------------- +# CMake file opencv-webcam-demo +# -------------- + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +set(subProject opencv-webcam-demo) + +PROJECT(${subProject}) + +file(GLOB SRCS *.c*) +file(GLOB HDRS *.h*) + +if( ${CMAKE_VERSION} VERSION_GREATER 2.8.11 ) + get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} DIRECTORY) # PATH was updated to DIRECTORY in 2.8.12 +else() + get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH) +endif() +set(COMMON_HDRS "${PARENT_DIR}/common/") +file(GLOB COMMON_HDRS_FILES ${COMMON_HDRS}/*.h*) + +add_executable(${subProject} ${SRCS} ${HDRS} ${COMMON_HDRS_FILES}) + +target_include_directories(${subProject} PRIVATE ${Boost_INCLUDE_DIRS} ${AFFDEX_INCLUDE_DIR} ${COMMON_HDRS}) + +target_link_libraries( ${subProject} ${AFFDEX_LIBRARIES} ${OpenCV_LIBS} ${Boost_LIBRARIES} ) + +#Add to the apps list +list( APPEND ${rootProject}_APPS ${subProject} ) +set( ${rootProject}_APPS ${${rootProject}_APPS} PARENT_SCOPE ) + +# Installation steps +install( TARGETS ${subProject} + RUNTIME DESTINATION ${RUNTIME_INSTALL_DIRECTORY} ) \ No newline at end of file diff --git a/opencv-webcam-demo/opencv-webcam-demo.cpp b/opencv-webcam-demo/opencv-webcam-demo.cpp index 9d6c05b..40aa2c2 100644 --- a/opencv-webcam-demo/opencv-webcam-demo.cpp +++ b/opencv-webcam-demo/opencv-webcam-demo.cpp @@ -52,7 +52,7 @@ int main(int argsc, char ** argsv) std::cerr.precision(precision); std::cout.precision(precision); - po::options_description description("Project for demoing the Windows SDK CameraDetector class (grabbing and processing frames from the camera)."); + po::options_description description("Project for demoing the Affdex SDK CameraDetector class (grabbing and processing frames from the camera)."); description.add_options() ("help,h", po::bool_switch()->default_value(false), "Display this help message.") #ifdef _WIN32 @@ -126,6 +126,7 @@ int main(int argsc, char ** argsv) //Initialize detectors frameDetector->setDetectAllEmotions(true); frameDetector->setDetectAllExpressions(true); + frameDetector->setDetectAllEmojis(true); frameDetector->setDetectGender(true); frameDetector->setDetectGlasses(true); frameDetector->setClassifierPath(DATA_FOLDER); diff --git a/video-demo/CMakeLists.txt b/video-demo/CMakeLists.txt new file mode 100755 index 0000000..3ed20b9 --- /dev/null +++ b/video-demo/CMakeLists.txt @@ -0,0 +1,34 @@ +# -------------- +# CMake file video-demo +# -------------- + +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +set(subProject video-demo) + +PROJECT(${subProject}) + +file(GLOB SRCS *.c*) +file(GLOB HDRS *.h*) + +if( ${CMAKE_VERSION} VERSION_GREATER 2.8.11 ) + get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} DIRECTORY) # PATH was updated to DIRECTORY in 2.8.12 +else() + get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH) +endif() +set(COMMON_HDRS "${PARENT_DIR}/common/") +file(GLOB COMMON_HDRS_FILES ${COMMON_HDRS}/*.h*) + +add_executable(${subProject} ${SRCS} ${HDRS} ${COMMON_HDRS_FILES}) + +target_include_directories(${subProject} PRIVATE ${Boost_INCLUDE_DIRS} ${AFFDEX_INCLUDE_DIR} ${COMMON_HDRS}) + +target_link_libraries( ${subProject} ${AFFDEX_LIBRARIES} ${OpenCV_LIBS} ${Boost_LIBRARIES} ) + +#Add to the apps list +list( APPEND ${rootProject}_APPS ${subProject} ) +set( ${rootProject}_APPS ${${rootProject}_APPS} PARENT_SCOPE ) + +# Installation steps +install( TARGETS ${subProject} + RUNTIME DESTINATION ${RUNTIME_INSTALL_DIRECTORY} ) \ No newline at end of file diff --git a/video-demo/video-demo.cpp b/video-demo/video-demo.cpp index 4e9d29d..6e2a1be 100644 --- a/video-demo/video-demo.cpp +++ b/video-demo/video-demo.cpp @@ -3,13 +3,14 @@ #include #include -#include +#include #include #include #include #include #include "VideoDetector.h" +#include "PhotoDetector.h" #include "AffdexException.h" #include "AFaceListener.hpp" @@ -20,13 +21,17 @@ using namespace std; using namespace affdex; -/// -/// Project demos how to use the Affdex Windows SDK VideoDetector -/// int main(int argsc, char ** argsv) { - //Defaults, overridden by the command line parameters + + + std::map VIDEO_EXTS = { {boost::filesystem::path(".avi"), 1}, + {boost::filesystem::path(".mov"), 1}, + {boost::filesystem::path(".flv"), 1}, + {boost::filesystem::path(".webm"), 1}, + {boost::filesystem::path(".wmv"), 1}, + {boost::filesystem::path(".mp4"), 1} }; affdex::path DATA_FOLDER; affdex::path LICENSE_PATH; affdex::path videoPath; @@ -34,14 +39,14 @@ int main(int argsc, char ** argsv) bool draw_display = true; bool loop = false; unsigned int nFaces = 1; - int faceDetectorMode = (int)FaceDetectorMode::SMALL_FACES; - + int faceDetectorMode = (int)FaceDetectorMode::LARGE_FACES; + const int precision = 2; std::cerr.precision(precision); std::cout.precision(precision); - + namespace po = boost::program_options; // abbreviate namespace - po::options_description description("Project for demoing the Windows SDK VideoDetector class (processing video files)."); + po::options_description description("Project for demoing the Affdex SDK VideoDetector class (processing video files)."); description.add_options() ("help,h", po::bool_switch()->default_value(false), "Display this help message.") #ifdef _WIN32 @@ -76,7 +81,7 @@ int main(int argsc, char ** argsv) std::cerr << "For help, use the -h option." << std::endl << std::endl; return 1; } - + // Parse and check the data folder (with assets) if (!boost::filesystem::exists(DATA_FOLDER)) { @@ -87,25 +92,37 @@ int main(int argsc, char ** argsv) } try { - //Initialize the video file detector - VideoDetector videoDetector(process_framerate, nFaces, (affdex::FaceDetectorMode) faceDetectorMode); - + std::shared_ptr detector; + //Initialize out file boost::filesystem::path csvPath(videoPath); + boost::filesystem::path fileExt = csvPath.extension(); csvPath.replace_extension(".csv"); std::ofstream csvFileStream(csvPath.c_str()); - + if (!csvFileStream.is_open()) { std::cerr << "Unable to open csv file " << csvPath << std::endl; return 1; } - - - - std::cout << "Max num of faces set to: " << videoDetector.getMaxNumberFaces() << std::endl; + + if (VIDEO_EXTS[fileExt]) // IF it is a video file. + { + detector = std::make_shared(process_framerate, nFaces, (affdex::FaceDetectorMode) faceDetectorMode); + } + else //Otherwise it's a photo + { + detector = std::make_shared(nFaces, (affdex::FaceDetectorMode) faceDetectorMode); + } + + + //VideoDetector videoDetector(process_framerate, nFaces, (affdex::FaceDetectorMode) faceDetectorMode); + + + + std::cout << "Max num of faces set to: " << detector->getMaxNumberFaces() << std::endl; std::string mode; - switch (videoDetector.getFaceDetectorMode()) + switch (detector->getFaceDetectorMode()) { case FaceDetectorMode::LARGE_FACES: mode = "LARGE_FACES"; @@ -116,66 +133,74 @@ int main(int argsc, char ** argsv) default: break; } - + std::cout << "Face detector mode set to: " << mode << std::endl; shared_ptr listenPtr(new PlottingImageListener(csvFileStream, draw_display)); - - //Activate all the detectors - videoDetector.setDetectAllEmotions(true); - videoDetector.setDetectAllExpressions(true); - videoDetector.setDetectGender(true); - videoDetector.setDetectGlasses(true); - //Set the location of the data folder and license file - videoDetector.setClassifierPath(DATA_FOLDER); - videoDetector.setLicensePath(LICENSE_PATH); - //Add callback functions implementations - videoDetector.setImageListener(listenPtr.get()); - - - videoDetector.start(); //Initialize the detectors .. call only once - + + detector->setDetectAllEmotions(true); + detector->setDetectAllExpressions(true); + detector->setDetectAllEmojis(true); + detector->setDetectGender(true); + detector->setDetectGlasses(true); + detector->setClassifierPath(DATA_FOLDER); + detector->setLicensePath(LICENSE_PATH); + detector->setImageListener(listenPtr.get()); + + + detector->start(); //Initialize the detectors .. call only once + do { shared_ptr videoListenPtr = std::make_shared(); - videoDetector.setProcessStatusListener(videoListenPtr.get()); - videoDetector.process(videoPath); //Process a video - - //For each frame processed - while (videoListenPtr->isRunning()) + detector->setProcessStatusListener(videoListenPtr.get()); + if (VIDEO_EXTS[fileExt]) + { + ((VideoDetector *)detector.get())->process(videoPath); //Process a video + } + else + { + //videoPath is of type std::wstring on windows, but std::string on other platforms. + cv::Mat img = cv::imread(std::string(videoPath.begin(), videoPath.end())); + + // Create a frame + Frame frame(img.size().width, img.size().height, img.data, Frame::COLOR_FORMAT::BGR); + + ((PhotoDetector *)detector.get())->process(frame); //Process an image + } + + do { if (listenPtr->getDataSize() > 0) { std::pair > dataPoint = listenPtr->getData(); Frame frame = dataPoint.first; std::map faces = dataPoint.second; - - - //Draw on the GUI + + if (draw_display) { listenPtr->draw(faces, frame); } - + std::cerr << "timestamp: " << frame.getTimestamp() << " cfps: " << listenPtr->getCaptureFrameRate() << " pfps: " << listenPtr->getProcessingFrameRate() << " faces: "<< faces.size() << endl; - - //Output metrics to file + listenPtr->outputToFile(faces, frame.getTimestamp()); } - } + } while(VIDEO_EXTS[fileExt] && videoListenPtr->isRunning()); } while(loop); - - videoDetector.stop(); + + detector->stop(); csvFileStream.close(); - + std::cout << "Output written to file: " << csvPath << std::endl; } catch (AffdexException ex) { std::cerr << ex.what(); } - + return 0; }