├── src ├── test │ ├── c++ │ │ ├── main.cpp │ │ ├── tst_pathdelegator.h │ │ ├── face-parts-service-test.pro │ │ ├── tst_getfaceresource.h │ │ ├── tst_pathdelegator.cpp │ │ ├── autotest.h │ │ └── tst_getfaceresource.cpp │ └── resources │ │ ├── 1.jpg │ │ ├── cmyk.jpg │ │ ├── profile.jpg │ │ ├── grayscale.jpg │ │ ├── 10156670_4.jpg │ │ ├── 48170621_4.jpg │ │ ├── cmykAlternate.jpg │ │ ├── profile.json │ │ ├── 1.json │ │ └── 10156670_4.json └── main │ ├── c++ │ ├── qtwebapp │ │ ├── bfLogging │ │ │ └── src │ │ │ │ ├── bfLogging.pri │ │ │ │ ├── dualfilelogger.cpp │ │ │ │ ├── dualfilelogger.h │ │ │ │ ├── logmessage.cpp │ │ │ │ ├── logmessage.h │ │ │ │ ├── filelogger.h │ │ │ │ ├── filelogger.cpp │ │ │ │ ├── logger.cpp │ │ │ │ └── logger.h │ │ └── bfHttpServer │ │ │ └── src │ │ │ ├── bfHttpServer.pri │ │ │ ├── httprequesthandler.cpp │ │ │ ├── httprequesthandler.h │ │ │ ├── httpconnectionhandlerpool.cpp │ │ │ ├── httpconnectionhandlerpool.h │ │ │ ├── httplistener.h │ │ │ ├── httplistener.cpp │ │ │ ├── staticfilecontroller.h │ │ │ ├── httpconnectionhandler.h │ │ │ ├── httpsessionstore.h │ │ │ ├── httpsession.h │ │ │ ├── httpcookie.h │ │ │ ├── httpsessionstore.cpp │ │ │ ├── httpresponse.cpp │ │ │ ├── httpsession.cpp │ │ │ ├── httpresponse.h │ │ │ ├── httpcookie.cpp │ │ │ ├── httpconnectionhandler.cpp │ │ │ ├── staticfilecontroller.cpp │ │ │ └── httprequest.h │ ├── weblogger.cpp │ ├── face-parts-service.pro │ ├── detect-face │ │ ├── eHimageFeature.h │ │ ├── eHutils.h │ │ ├── eHshiftdt.h │ │ ├── eHfeatpyramid.h │ │ ├── eHfilter.h │ │ ├── eHutils.cpp │ │ ├── eHbbox.h │ │ ├── eHmatrix.h │ │ ├── eHmatrix.cpp │ │ ├── eHbox.cpp │ │ ├── eHbox.h │ │ ├── eHposemodel.h │ │ ├── eHbbox.cpp │ │ ├── eHfacemodel.h │ │ ├── eHfeatpyramid.cpp │ │ ├── eHimage.h │ │ ├── eHfilter.cpp │ │ ├── eHshiftdt.cpp │ │ └── eHimageFeature.cpp │ ├── weblogger.h │ ├── httpheaders.cpp │ ├── httpheaders.h │ ├── webservice │ │ ├── ecvresource.h │ │ ├── pathdelegator.h │ │ ├── ecvresource.cpp │ │ ├── pathdelegator.cpp │ │ └── getfaceresource.h │ ├── main.cpp │ └── face-parts-service.pri │ └── resources │ └── configfile.ini ├── .gitignore ├── LICENSE.txt ├── images ├── regular.jpg └── postman_demo.png ├── .travis.yml ├── README.md └── pom.xml /src/test/c++/main.cpp: -------------------------------------------------------------------------------- 1 | #include "autotest.h" 2 | 3 | TEST_MAIN 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | *.autosave 3 | *.log 4 | *.iml 5 | 6 | target/ 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/LICENSE.txt -------------------------------------------------------------------------------- /images/regular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/images/regular.jpg -------------------------------------------------------------------------------- /images/postman_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/images/postman_demo.png -------------------------------------------------------------------------------- /src/test/resources/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/1.jpg -------------------------------------------------------------------------------- /src/test/resources/cmyk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/cmyk.jpg -------------------------------------------------------------------------------- /src/test/resources/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/profile.jpg -------------------------------------------------------------------------------- /src/test/resources/grayscale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/grayscale.jpg -------------------------------------------------------------------------------- /src/test/resources/10156670_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/10156670_4.jpg -------------------------------------------------------------------------------- /src/test/resources/48170621_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/48170621_4.jpg -------------------------------------------------------------------------------- /src/test/resources/cmykAlternate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eHarmony/face-parts-service/HEAD/src/test/resources/cmykAlternate.jpg -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/bfLogging.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | DEPENDPATH += $$PWD 3 | 4 | HEADERS += $$PWD/logmessage.h $$PWD/logger.h $$PWD/filelogger.h $$PWD/dualfilelogger.h 5 | SOURCES += $$PWD/logmessage.cpp $$PWD/logger.cpp $$PWD/filelogger.cpp $$PWD/dualfilelogger.cpp 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: 3 | - sudo apt-get update 4 | - sudo apt-get install libatlas-base-dev 5 | - sudo apt-get install libjpeg-dev 6 | - sudo apt-get install libtbb-dev 7 | - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa 8 | - sudo apt-get update -qq 9 | - sudo apt-get install -qq qt5-default 10 | notifications: 11 | email: 12 | - jmorra@eharmony.com 13 | -------------------------------------------------------------------------------- /src/main/c++/weblogger.cpp: -------------------------------------------------------------------------------- 1 | #include "weblogger.h" 2 | 3 | FileLogger* WebLogger::logger = NULL; 4 | 5 | void WebLogger::setFileLogger(FileLogger *fileLogger) { 6 | WebLogger::logger = fileLogger; 7 | } 8 | 9 | void WebLogger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) { 10 | if (logger) { 11 | logger->log(type, message, file, function, line); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/c++/face-parts-service.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-04-07T10:03:59 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core network 8 | 9 | QT -= gui 10 | 11 | TARGET = face-parts-service 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | include(face-parts-service.pri) 18 | 19 | SOURCES += main.cpp 20 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHimageFeature.h: -------------------------------------------------------------------------------- 1 | /** @file eHimageFeature.h 2 | * @brief Compute image features 3 | * 4 | * @author Hang Su 5 | * @date 2012-08 6 | */ 7 | #include "eHimage.h" 8 | #include "eHmatrix.h" 9 | 10 | /** @brief Compute HOG feature of a color image 11 | * @param img 3 channel double precision image 12 | * @param sbin bin size 13 | * @return HOG feature matrix 14 | * @note feature matrix is allocated inside, proper delete is necessary after use 15 | */ 16 | mat3d_t* eHhog(const image_t* img, int sbin); 17 | -------------------------------------------------------------------------------- /src/main/c++/weblogger.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBLOGGER_H 2 | #define WEBLOGGER_H 3 | 4 | #include 5 | 6 | class WebLogger 7 | { 8 | public: 9 | static void setFileLogger(FileLogger* fileLogger); 10 | static void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0); 11 | 12 | private: 13 | WebLogger(); 14 | WebLogger(WebLogger const&); 15 | void operator=(WebLogger const&); 16 | 17 | static FileLogger *logger; 18 | }; 19 | 20 | #endif // WEBLOGGER_H 21 | -------------------------------------------------------------------------------- /src/main/c++/httpheaders.cpp: -------------------------------------------------------------------------------- 1 | #include "httpheaders.h" 2 | 3 | const QByteArray HttpHeaders::ACCEPT = "accept"; 4 | const QByteArray HttpHeaders::CONTENT_TYPE = "content-type"; 5 | const QByteArray HttpHeaders::CONTENT_LENGTH = "content-length"; 6 | const QByteArray HttpHeaders::CACHE_CONTROL = "cache-control"; 7 | const QByteArray HttpHeaders::NO_CACHE = "no-cache"; 8 | 9 | const int HttpHeaders::STATUS_SUCCESS = 200; 10 | const int HttpHeaders::STATUS_NOT_FOUND = 404; 11 | const int HttpHeaders::STATUS_PRECONDITION_FAILED = 412; 12 | const int HttpHeaders::STATUS_UNSUPPORTED_MEDIA_TYPE = 415; 13 | const int HttpHeaders::STATUS_ERROR = 500; 14 | -------------------------------------------------------------------------------- /src/test/c++/tst_pathdelegator.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_PATHDELEGATOR_H 2 | #define TST_PATHDELEGATOR_H 3 | 4 | #include 5 | #include 6 | 7 | class PathDelegator; 8 | class GetFaceResource; 9 | 10 | class PathDelegatorTest : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | private: 15 | PathDelegator* pathDelegator; 16 | GetFaceResource* faceResource; 17 | 18 | private Q_SLOTS: 19 | void initTestCase(); 20 | void cleanupTestCase(); 21 | void testRemoveExtension(); 22 | 23 | void noPath(); 24 | void goodPath(); 25 | }; 26 | 27 | DECLARE_TEST(PathDelegatorTest) 28 | 29 | #endif // TST_PATHDELEGATOR_H 30 | -------------------------------------------------------------------------------- /src/main/c++/httpheaders.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPHEADERS_H 2 | #define HTTPHEADERS_H 3 | 4 | #include 5 | 6 | class HttpHeaders 7 | { 8 | public: 9 | const static QByteArray CONTENT_TYPE; 10 | const static QByteArray ACCEPT; 11 | const static QByteArray CONTENT_LENGTH; 12 | const static QByteArray CACHE_CONTROL; 13 | const static QByteArray NO_CACHE; 14 | const static int STATUS_SUCCESS; 15 | const static int STATUS_NOT_FOUND; 16 | const static int STATUS_PRECONDITION_FAILED; 17 | const static int STATUS_UNSUPPORTED_MEDIA_TYPE; 18 | const static int STATUS_ERROR; 19 | }; 20 | 21 | #endif // HTTPHEADERS_H 22 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/dualfilelogger.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "dualfilelogger.h" 7 | 8 | 9 | DualFileLogger::DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval, QObject* parent) 10 | :Logger(parent) 11 | { 12 | firstLogger=new FileLogger(firstSettings, refreshInterval, this); 13 | secondLogger=new FileLogger(secondSettings, refreshInterval, this); 14 | } 15 | 16 | 17 | void DualFileLogger::log(const QtMsgType type, const QString& message) { 18 | firstLogger->log(type, message); 19 | secondLogger->log(type, message); 20 | } 21 | -------------------------------------------------------------------------------- /src/test/c++/face-parts-service-test.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-04-08T14:04:44 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += network testlib 8 | 9 | QT -= gui 10 | 11 | TARGET = face-parts-service-test 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | include(../../main/c++/face-parts-service.pri) 18 | 19 | SOURCES += tst_pathdelegator.cpp \ 20 | main.cpp \ 21 | tst_getfaceresource.cpp 22 | 23 | DEFINES += SRCDIR=\\\"$$PWD/\\\" 24 | 25 | HEADERS += \ 26 | tst_pathdelegator.h \ 27 | autotest.h \ 28 | tst_getfaceresource.h 29 | -------------------------------------------------------------------------------- /src/main/c++/webservice/ecvresource.h: -------------------------------------------------------------------------------- 1 | #ifndef ECVRESOURCE_H 2 | #define ECVRESOURCE_H 3 | 4 | #include 5 | 6 | class ECVResource : public HttpRequestHandler 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit ECVResource(QFile* ecvFile, QObject *parent = 0); 11 | virtual ~ECVResource(); 12 | void service(HttpRequest &request, HttpResponse &response); 13 | 14 | private: 15 | QFile* ecvFile; 16 | 17 | bool isServerUp() const; 18 | static const QByteArray SERVER_UP_TEXT; 19 | static const QByteArray SERVER_DOWN_TEXT; 20 | static const int SERVER_UP_STATUS; 21 | static const int SERVER_DOWN_STATUS; 22 | 23 | }; 24 | 25 | #endif // ECVRESOURCE_H 26 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/bfHttpServer.pri: -------------------------------------------------------------------------------- 1 | INCLUDEPATH += $$PWD 2 | DEPENDPATH += $$PWD 3 | 4 | HEADERS += $$PWD/httplistener.h $$PWD/httpconnectionhandler.h $$PWD/httpconnectionhandlerpool.h $$PWD/httprequest.h $$PWD/httpresponse.h $$PWD/httpcookie.h $$PWD/httprequesthandler.h 5 | HEADERS += $$PWD/httpsession.h $$PWD/httpsessionstore.h 6 | HEADERS += $$PWD/staticfilecontroller.h 7 | 8 | SOURCES += $$PWD/httplistener.cpp $$PWD/httpconnectionhandler.cpp $$PWD/httpconnectionhandlerpool.cpp $$PWD/httprequest.cpp $$PWD/httpresponse.cpp $$PWD/httpcookie.cpp $$PWD/httprequesthandler.cpp 9 | SOURCES += $$PWD/httpsession.cpp $$PWD/httpsessionstore.cpp 10 | SOURCES += $$PWD/staticfilecontroller.cpp 11 | -------------------------------------------------------------------------------- /src/main/c++/webservice/pathdelegator.h: -------------------------------------------------------------------------------- 1 | #ifndef PATHDELEGATOR_H 2 | #define PATHDELEGATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class PathDelegator : public HttpRequestHandler 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit PathDelegator(QObject *parent = 0); 14 | virtual ~PathDelegator(); 15 | void service(HttpRequest &request, HttpResponse &response); 16 | void addPath(const QByteArray& path, HttpRequestHandler* handler); 17 | QByteArray removeExtension(const QByteArray& path) const; 18 | 19 | private: 20 | QMap paths; 21 | 22 | }; 23 | 24 | #endif // PATHDELEGATOR_H 25 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httprequesthandler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httprequesthandler.h" 7 | 8 | HttpRequestHandler::HttpRequestHandler(QObject* parent) 9 | : QObject(parent) 10 | {} 11 | 12 | HttpRequestHandler::~HttpRequestHandler() {} 13 | 14 | void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) { 15 | qCritical("HttpRequestHandler: you need to override the dispatch() function"); 16 | qDebug("HttpRequestHandler: request=%s %s %s",request.getMethod().data(),request.getPath().data(),request.getVersion().data()); 17 | response.setStatus(501,"not implemented"); 18 | response.write("501 not implemented",true); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHutils.h: -------------------------------------------------------------------------------- 1 | /** @file eHutils.h 2 | * @brief Some useful stuff (string parsing etc. ) 3 | * 4 | * @author Hang Su 5 | * @date 2012-08 6 | */ 7 | #ifndef EHUTILS_H 8 | #define EHUTILS_H 9 | 10 | /** @brief Parse given string(e.g. "10, 5\0") to integer array 11 | * @param csvstr null-terminated c string 12 | * @param siz number of integers inside the string; if -1 is passed, actural 13 | * size will be calculated and stored in siz 14 | * @return array of integers, memory is allocated for it 15 | */ 16 | int* parseCSVstr2int(const char* csvstr, int* siz, int offset = 0); 17 | 18 | /** @brief Parse given string(e.g. "1.2, 3.4\0") to double precision array 19 | * @param csvstr null-terminated c string 20 | * @param siz amount of numbers inside the string; if -1 is passed, actural 21 | * size will be calculated and stored in siz 22 | * @return array of numbers, memory is allocated for it 23 | */ 24 | double* parseCSVstr2double(const char* csvstr, int* siz); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/test/c++/tst_getfaceresource.h: -------------------------------------------------------------------------------- 1 | #ifndef TST_GETFACERESOURCE_H 2 | #define TST_GETFACERESOURCE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class GetFaceResource; 9 | 10 | class GetFaceResourceTest : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | private: 15 | GetFaceResource *faceResource; 16 | 17 | void testNoFaces(const QString& fileName); 18 | void testFaces(const QString& fileName); 19 | void compareDoubles(double actual, double expected, double delta); 20 | void printSVG(const QString& fileName); 21 | QString testResources; 22 | 23 | private Q_SLOTS: 24 | void initTestCase(); 25 | void cleanupTestCase(); 26 | 27 | void cmyk(); 28 | void cmykAlternate(); 29 | void goodFace(); 30 | void profile(); 31 | void faceFromPose(); 32 | void badFile(); 33 | void noFile(); 34 | void noFace(); 35 | void grayscale(); 36 | }; 37 | 38 | DECLARE_TEST(GetFaceResourceTest) 39 | 40 | #endif // TST_GETFACERESOURCE_H 41 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHshiftdt.h: -------------------------------------------------------------------------------- 1 | /** @file eHshiftdt.h 2 | * 3 | * @brief Generalized Distance Transform 4 | * 5 | * @sa P. F. Felzenszwalb and D. P. Huttenlocher, "Distance Transforms of Sampled Functions". 2004. 6 | * @author Hang Su 7 | * @date 2012-07 8 | */ 9 | 10 | /** @brief Perform generalized distance transform 11 | * 12 | * This applies computes a min convolution of a quadratic function ax^2+bx 13 | * This outputs results on a shifted(offy, offx), subsampled(dstep) grid 14 | * @note M, Ix, Iy should be properly allocated before passed in, 15 | * they are then modified as output results 16 | */ 17 | void eHshiftdt(double* M, int* Ix, int* Iy, 18 | int lenx, int leny, int offx, int offy, int dstep, 19 | const double* vals, int sizx, int sizy, 20 | const double* w, bool multiThreaded = true); 21 | 22 | /* wrapper for default setting */ 23 | void eHshiftdt(double* M, int* Ix, int* Iy, 24 | const double* vals, int sizx, int sizy, 25 | const double* w, bool multiThreaded = true); 26 | -------------------------------------------------------------------------------- /src/main/resources/configfile.ini: -------------------------------------------------------------------------------- 1 | # HTTP properties 2 | port=8084 3 | minThreads=1 4 | maxThreads=100 5 | cleanupInterval=1000 6 | readTimeout=10000 7 | maxRequestSize=52428800 8 | maxMultiPartSize=52428800 9 | 10 | # Logger options 11 | fileName=face-parts.log 12 | maxSize=1000000 13 | maxBackups=2 14 | minLevel=0 15 | msgformat={timestamp} {typeNr} {type} thread={thread}: {msg} 16 | timestampFormat=dd.MM.yyyy hh:mm:ss.zzz 17 | bufferSize=0 18 | 19 | # Model file locations 20 | faceModel=src/main/resources/face_p146.xml 21 | poseModel=src/main/resources/pose_BUFFY.xml 22 | frontalPoints=68 23 | profilePoints=39 24 | 25 | # File to determine if server is up or down 26 | ecvFile=ecv.txt 27 | 28 | # Properties that define how images are displayed 29 | faceColor=255,0,0 30 | foregroundTextColor=0,0,0 31 | pointColor=0,255,0 32 | poseTextSize=20 33 | numberTextSize=15 34 | 35 | # Good response image versions 36 | imageResponses=jpg,jpeg 37 | 38 | # Uploads are done using multipart-form. This sets the part name of the uploaded image 39 | uploadedFileName=file 40 | -------------------------------------------------------------------------------- /src/main/c++/webservice/ecvresource.cpp: -------------------------------------------------------------------------------- 1 | #include "ecvresource.h" 2 | 3 | const QByteArray ECVResource::SERVER_UP_TEXT = "SERVER UP"; 4 | const QByteArray ECVResource::SERVER_DOWN_TEXT = "SERVER DOWN"; 5 | const int ECVResource::SERVER_UP_STATUS = 200; 6 | const int ECVResource::SERVER_DOWN_STATUS = 503; 7 | 8 | ECVResource::ECVResource(QFile* ecvFile, QObject *parent) : 9 | HttpRequestHandler(parent) { 10 | this->ecvFile = ecvFile; 11 | } 12 | 13 | ECVResource::~ECVResource() { 14 | if (ecvFile != NULL) { 15 | delete ecvFile; 16 | } 17 | } 18 | 19 | void ECVResource::service(HttpRequest &request, HttpResponse &response) { 20 | Q_UNUSED(request); 21 | if (isServerUp()) { 22 | response.setStatus(SERVER_UP_STATUS, SERVER_UP_TEXT); 23 | response.write(SERVER_UP_TEXT); 24 | } 25 | else { 26 | response.setStatus(SERVER_DOWN_STATUS, SERVER_DOWN_TEXT); 27 | response.write(SERVER_DOWN_TEXT); 28 | } 29 | } 30 | 31 | bool ECVResource::isServerUp() const { 32 | if (ecvFile == NULL) { 33 | return true; 34 | } 35 | return !ecvFile->exists(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHfeatpyramid.h: -------------------------------------------------------------------------------- 1 | /** @file eHfeatpyramid.h 2 | * @brief Feature pyramid data type and calculation 3 | * 4 | * @author Hang Su 5 | * @date 2012-08-14 6 | */ 7 | #ifndef EHFEATPYRAMID_H 8 | #define EHFEATPYRAMID_H 9 | 10 | #include "eHmatrix.h" 11 | #include "eHimage.h" 12 | 13 | /** @struct featpyra_t 14 | * @brief Image feature pyramid 15 | */ 16 | struct featpyra_t { 17 | mat3d_t** feat; /**< @brief features of each level */ 18 | double* scale; /**< @brief scaled of each level */ 19 | int len; /**< @brief levels of pyra, size of feat & scale */ 20 | int interval; /**< @brief levels within a double-size interval */ 21 | int imy; /**< @brief image height */ 22 | int imx; /**< @brief image width */ 23 | }; 24 | 25 | /** @brief Allocate and compute a feature pyramid from an image 26 | * @param hallucinate whether hallucinate a higher resolution interval 27 | */ 28 | featpyra_t* featpyra_create(const image_t* im, int interval, int sbin, const int* maxsize, bool hallucinate=true); 29 | 30 | /** @brief Delete a feature pyramid 31 | */ 32 | void featpyra_delete(featpyra_t* pyra); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHfilter.h: -------------------------------------------------------------------------------- 1 | /** @file eHfilter.h 2 | * @brief Filters applied on image features 3 | * 4 | * @author Hang Su 5 | * @date 2012-08 6 | */ 7 | #ifndef EHFILTER_H 8 | #define EHFILTER_H 9 | 10 | #include "eHmatrix.h" 11 | #include 12 | 13 | /** @struct eHfilter 14 | * @brief Image feature filter 15 | */ 16 | struct filter_t { 17 | int i; /**< @brief filter index, not used */ 18 | mat3d_t w; /**< @brief filter [y,x,f], where (y,x) is location, f is feature index */ 19 | }; 20 | 21 | /** @brief Convolve a feature map with a set of filters - Multithreaded version 22 | * @param filters a set of part filters 23 | * @param feats feature map 24 | * @param start range of filters used - first one 25 | * @param end range of filters used - last one 26 | * @param optional parameter to state whether to use multithreaded version 27 | * @return filter responses 28 | * @note filter responses is allocated inside, proper delete is necessary after use 29 | * @note cblas library is required 30 | * @sa filterv_apply_ST() 31 | */ 32 | mat3d_t* filterv_apply(const std::vector filters, const mat3d_t* feats, int start, int end, bool multithreaded = true); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/main/c++/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char** argv) { 11 | QCoreApplication app(argc,argv); 12 | 13 | QSettings settings(argv[1], QSettings::IniFormat, &app); 14 | 15 | facemodel_t* faceModel = facemodel_readFromFile(settings.value("faceModel").toString().toStdString().c_str()); 16 | posemodel_t* poseModel = posemodel_readFromFile(settings.value("poseModel").toString().toStdString().c_str()); 17 | 18 | FileLogger logger(&settings); 19 | WebLogger::setFileLogger(&logger); 20 | 21 | QFile *ecvFile = NULL; 22 | if (settings.contains("ecv_file")) { 23 | ecvFile = new QFile(settings.value("ecvFile").toString()); 24 | } 25 | PathDelegator pathDelegator; 26 | pathDelegator.addPath("/face-parts/generate", new GetFaceResource(faceModel, poseModel, settings)); 27 | pathDelegator.addPath("/face-parts/ecv", new ECVResource(ecvFile)); 28 | 29 | new HttpListener(&settings, &pathDelegator, &app); 30 | 31 | return app.exec(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHutils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHutils.cpp 3 | * 4 | * Hang Su 5 | * 2012-08 @ eH 6 | */ 7 | #include "eHutils.h" 8 | #include "string.h" 9 | #include "stdlib.h" 10 | 11 | int* parseCSVstr2int(const char* csvstr, int* siz, int offset) { 12 | /* if size is not given, first find it out*/ 13 | char delimiter = ','; 14 | const char* ptr; 15 | if(*siz==-1){ 16 | *siz=0; 17 | ptr=strchr(csvstr,delimiter); 18 | while(ptr!=NULL){ 19 | (*siz)++; 20 | ptr = strchr(ptr+1,delimiter); 21 | } 22 | (*siz) += 1; 23 | } 24 | 25 | int* arr = new int[*siz]; 26 | 27 | char* endptr; 28 | arr[0] = strtol(csvstr, &endptr, 10) + offset; 29 | for(int i=1;i<*siz;i++) { 30 | arr[i] = strtol(endptr+1, &endptr, 10) + offset; 31 | } 32 | return arr; 33 | } 34 | 35 | double* parseCSVstr2double(const char* csvstr, int *siz) { /* if size is not given, first find it out*/ 36 | char delimiter = ','; 37 | const char* ptr; 38 | if(*siz==-1){ 39 | *siz=0; 40 | ptr=strchr(csvstr,delimiter); 41 | while(ptr!=NULL){ 42 | (*siz)++; 43 | ptr = strchr(ptr+1,delimiter); 44 | } 45 | (*siz) += 1; 46 | } 47 | 48 | double* arr = new double[*siz]; 49 | 50 | char* endptr; 51 | arr[0] = strtod(csvstr, &endptr); 52 | for(int i=1;i<*siz;i++) { 53 | arr[i] = strtod(endptr+1, &endptr); 54 | } 55 | return arr; 56 | } 57 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httprequesthandler.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPREQUESTHANDLER_H 7 | #define HTTPREQUESTHANDLER_H 8 | 9 | #include "httprequest.h" 10 | #include "httpresponse.h" 11 | 12 | /** 13 | The request handler generates a response for each HTTP request. Web Applications 14 | usually have one central request handler that maps incoming requests to several 15 | controllers (servlets) based on the requested path. 16 |

17 | You need to override the service() method or you will always get an HTTP error 501. 18 |

19 | @warning Be aware that the main request handler instance must be created on the heap and 20 | that it is used by multiple threads simultaneously. 21 | @see StaticFileController which delivers static local files. 22 | */ 23 | 24 | class HttpRequestHandler : public QObject { 25 | Q_OBJECT 26 | Q_DISABLE_COPY(HttpRequestHandler) 27 | public: 28 | 29 | /** Constructor */ 30 | HttpRequestHandler(QObject* parent=0); 31 | 32 | /** Destructor */ 33 | virtual ~HttpRequestHandler(); 34 | 35 | /** 36 | Generate a response for an incoming HTTP request. 37 | @param request The received HTTP request 38 | @param response Must be used to return the response 39 | @warning This method must be thread safe 40 | */ 41 | virtual void service(HttpRequest& request, HttpResponse& response); 42 | 43 | }; 44 | 45 | #endif // HTTPREQUESTHANDLER_H 46 | -------------------------------------------------------------------------------- /src/test/c++/tst_pathdelegator.cpp: -------------------------------------------------------------------------------- 1 | #include "tst_pathdelegator.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void PathDelegatorTest::initTestCase() { 10 | pathDelegator = new PathDelegator(); 11 | QSettings settings; 12 | faceResource = new GetFaceResource(NULL, NULL, settings); 13 | 14 | pathDelegator->addPath("/face-parts/generate", faceResource); 15 | } 16 | 17 | void PathDelegatorTest::cleanupTestCase() { 18 | delete pathDelegator; 19 | } 20 | 21 | void PathDelegatorTest::noPath() { 22 | QSettings settings; 23 | HttpRequest request(&settings); 24 | HttpResponse response(NULL); 25 | request.setPath("/foo"); 26 | 27 | pathDelegator->service(request, response); 28 | 29 | QCOMPARE(HttpHeaders::STATUS_NOT_FOUND, response.getStatus()); 30 | } 31 | 32 | void PathDelegatorTest::goodPath() { 33 | QSettings settings; 34 | HttpRequest request(&settings); 35 | HttpResponse response(NULL); 36 | 37 | request.setPath("/face-parts/generate"); 38 | pathDelegator->service(request, response); 39 | 40 | QCOMPARE(HttpHeaders::STATUS_PRECONDITION_FAILED, response.getStatus()); 41 | } 42 | 43 | void PathDelegatorTest::testRemoveExtension() { 44 | QCOMPARE(QString("abc"), QString(pathDelegator->removeExtension("abc.jpg"))); 45 | QCOMPARE(QString("abc"), QString(pathDelegator->removeExtension("abc"))); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/c++/webservice/pathdelegator.cpp: -------------------------------------------------------------------------------- 1 | #include "pathdelegator.h" 2 | #include 3 | #include 4 | 5 | PathDelegator::PathDelegator(QObject *parent) : 6 | HttpRequestHandler(parent) { 7 | } 8 | 9 | PathDelegator::~PathDelegator() { 10 | for (QMap::iterator iter = paths.begin(); iter != paths.end(); ++iter) { 11 | if (iter.value()) { 12 | delete iter.value(); 13 | } 14 | } 15 | } 16 | 17 | /** 18 | * @brief PathDelegator::service -- Delegate the request to the service designed to handle that request 19 | * @param request 20 | * @param response 21 | */ 22 | void PathDelegator::service(HttpRequest &request, HttpResponse &response) { 23 | QByteArray path = removeExtension(request.getPath()); 24 | if (paths.contains(path)) { 25 | paths[path]->service(request, response); 26 | } 27 | else { 28 | response.setStatus(HttpHeaders::STATUS_NOT_FOUND, QByteArray("Cannot find ") + path); 29 | } 30 | WebLogger::log(QtWarningMsg, QByteArray::number(response.getStatus()) + ": " + response.getStatusText(), "pathdelegator", "service"); 31 | } 32 | 33 | /** 34 | * @brief PathDelegator::addPath -- Add a service at a specific path 35 | * @param path 36 | * @param handler 37 | */ 38 | void PathDelegator::addPath(const QByteArray &path, HttpRequestHandler *handler) { 39 | paths.insert(path, handler); 40 | } 41 | 42 | /** 43 | * @brief PathDelegator::removeExtension -- A helper method to remove the extension from a path 44 | * @param path 45 | * @return the path with the extension removed. 46 | */ 47 | QByteArray PathDelegator::removeExtension(const QByteArray &path) const { 48 | if (path.contains(".")) { 49 | return path.left(path.indexOf(".")); 50 | } 51 | return path; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/c++/face-parts-service.pri: -------------------------------------------------------------------------------- 1 | include(qtwebapp/bfHttpServer/src/bfHttpServer.pri) 2 | include(qtwebapp/bfLogging/src/bfLogging.pri) 3 | 4 | INCLUDEPATH += $$PWD 5 | DEPENDPATH += $$PWD 6 | 7 | DEFINES+=cimg_display=0 8 | DEFINES+=cimg_use_jpeg 9 | 10 | HEADERS += \ 11 | $$PWD/rapidxml.hpp \ 12 | $$PWD/CImg.h \ 13 | $$PWD/detect-face/eHbbox.h \ 14 | $$PWD/detect-face/eHbox.h \ 15 | $$PWD/detect-face/eHfacemodel.h \ 16 | $$PWD/detect-face/eHfeatpyramid.h \ 17 | $$PWD/detect-face/eHfilter.h \ 18 | $$PWD/detect-face/eHimage.h \ 19 | $$PWD/detect-face/eHimageFeature.h \ 20 | $$PWD/detect-face/eHmatrix.h \ 21 | $$PWD/detect-face/eHposemodel.h \ 22 | $$PWD/detect-face/eHshiftdt.h \ 23 | $$PWD/detect-face/eHutils.h \ 24 | $$PWD/webservice/pathdelegator.h \ 25 | $$PWD/webservice/getfaceresource.h \ 26 | $$PWD/weblogger.h \ 27 | $$PWD/webservice/ecvresource.h \ 28 | $$PWD/httpheaders.h 29 | 30 | SOURCES += \ 31 | $$PWD/detect-face/eHbbox.cpp \ 32 | $$PWD/detect-face/eHbox.cpp \ 33 | $$PWD/detect-face/eHfacemodel.cpp \ 34 | $$PWD/detect-face/eHfeatpyramid.cpp \ 35 | $$PWD/detect-face/eHfilter.cpp \ 36 | $$PWD/detect-face/eHimage.cpp \ 37 | $$PWD/detect-face/eHimageFeature.cpp \ 38 | $$PWD/detect-face/eHmatrix.cpp \ 39 | $$PWD/detect-face/eHposemodel.cpp \ 40 | $$PWD/detect-face/eHshiftdt.cpp \ 41 | $$PWD/detect-face/eHutils.cpp \ 42 | $$PWD/webservice/pathdelegator.cpp \ 43 | $$PWD/webservice/getfaceresource.cpp \ 44 | $$PWD/weblogger.cpp \ 45 | $$PWD/webservice/ecvresource.cpp \ 46 | $$PWD/httpheaders.cpp 47 | 48 | macx { 49 | INCLUDEPATH += /usr/local/include /opt/local/include /usr/local/include/tbb/ 50 | LIBS += -L/usr/local/lib 51 | } 52 | 53 | unix:!macx { 54 | INCLUDEPATH += /usr/include /usr/include/tbb 55 | LIBS += -L/usr/lib64/atlas/ -L/usr/lib64 56 | } 57 | 58 | LIBS += -ljpeg -lcblas -ltbb 59 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/dualfilelogger.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef DUALFILELOGGER_H 7 | #define DUALFILELOGGER_H 8 | 9 | #include "logger.h" 10 | #include "filelogger.h" 11 | #include 12 | #include 13 | #include 14 | 15 | /** 16 | Logs messages into two log files simultaneously. 17 | May be used to create two logfiles with different configuration settings. 18 | @see FileLogger for a description of the two underlying loggers. 19 | */ 20 | 21 | class DualFileLogger : public Logger { 22 | Q_OBJECT 23 | Q_DISABLE_COPY(DualFileLogger) 24 | public: 25 | 26 | /** 27 | Constructor. 28 | @param firstSettings Configuration settings for the first log file, usually stored in an INI file. 29 | Must not be 0. 30 | Settings are read from the current group, so the caller must have called settings->beginGroup(). 31 | Because the group must not change during runtime, it is recommended to provide a 32 | separate QSettings instance to the logger that is not used by other parts of the program. 33 | @param secondSettings Same as firstSettings, but for the second log file. 34 | @param refreshInterval Interval of checking for changed config settings in msec, or 0=disabled 35 | @param parent Parent object. 36 | */ 37 | DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval=10000, QObject *parent = 0); 38 | 39 | using Logger::log; 40 | /** 41 | Decorate and log a message. 42 | This method is thread safe. 43 | @param type Message type (level) 44 | @param message Message text 45 | @see LogMessage for a description of the message decoration. 46 | */ 47 | virtual void log(const QtMsgType type, const QString& message); 48 | 49 | private: 50 | 51 | /** First logger */ 52 | FileLogger* firstLogger; 53 | 54 | /** Second logger */ 55 | FileLogger* secondLogger; 56 | 57 | }; 58 | 59 | #endif // DUALFILELOGGER_H 60 | -------------------------------------------------------------------------------- /src/test/c++/autotest.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTOTEST_H 2 | #define AUTOTEST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace AutoTest 10 | { 11 | typedef QList TestList; 12 | 13 | inline TestList& testList() 14 | { 15 | static TestList list; 16 | return list; 17 | } 18 | 19 | inline bool findObject(QObject* object) 20 | { 21 | TestList& list = testList(); 22 | if (list.contains(object)) 23 | { 24 | return true; 25 | } 26 | foreach (QObject* test, list) 27 | { 28 | if (test->objectName() == object->objectName()) 29 | { 30 | return true; 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | inline void addTest(QObject* object) 37 | { 38 | TestList& list = testList(); 39 | if (!findObject(object)) 40 | { 41 | list.append(object); 42 | } 43 | } 44 | 45 | inline int run(int argc, char *argv[]) 46 | { 47 | int ret = 0; 48 | 49 | foreach (QObject* test, testList()) 50 | { 51 | for (int i=0; isetProperty(split[0].toStdString().c_str(), split[1]); 56 | } 57 | } 58 | ret += QTest::qExec(test, 0, NULL); 59 | } 60 | 61 | return ret; 62 | } 63 | } 64 | 65 | template 66 | class Test 67 | { 68 | public: 69 | QSharedPointer child; 70 | 71 | Test(const QString& name) : child(new T) 72 | { 73 | child->setObjectName(name); 74 | AutoTest::addTest(child.data()); 75 | } 76 | }; 77 | 78 | #define DECLARE_TEST(className) static Test t(#className); 79 | 80 | #define TEST_MAIN \ 81 | int main(int argc, char *argv[]) \ 82 | { \ 83 | return AutoTest::run(argc, argv); \ 84 | } 85 | 86 | #endif // AUTOTEST_H 87 | -------------------------------------------------------------------------------- /src/main/c++/webservice/getfaceresource.h: -------------------------------------------------------------------------------- 1 | #ifndef GETFACERESOURCE_H 2 | #define GETFACERESOURCE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class GetFaceResource : public HttpRequestHandler 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit GetFaceResource(facemodel_t* faceModel, posemodel_t* poseModel, const QSettings& settings, QObject *parent = 0); 15 | virtual ~GetFaceResource(); 16 | void service(HttpRequest &request, HttpResponse &response); 17 | int getJSONFaces(QFile *file, QJsonDocument& document, bool writeJustPoints=false); 18 | 19 | private: 20 | facemodel_t* faceModel; 21 | posemodel_t* poseModel; 22 | 23 | QJsonObject getProfileParts(const bbox_t& faceBox); 24 | QJsonObject getFrontalParts(const bbox_t& faceBox); 25 | QJsonObject getUnknownParts(const bbox_t& faceBox); 26 | QJsonObject extractPart(const bbox_t &faceBox, const size_t i); 27 | QJsonArray extractParts(const bbox_t &faceBox, const size_t start, const size_t end); 28 | int getFaceBoxes(QFile *file, std::vector& faceBoxes); 29 | int drawFacesOnImage(QFile *file, QByteArray& imageBytes, const QByteArray& extension); 30 | int saveFacesOnImage(const cimg_library::CImg& img, QByteArray& imageBytes, const QByteArray& extension); 31 | QByteArray generateErrorMessage(QFile *file) const; 32 | 33 | const unsigned char* initArray(const QStringList& values); 34 | 35 | const int profilePoints; 36 | const int frontalPoints; 37 | const QStringList imageExtensions; 38 | const QByteArray uploadedFileName; 39 | const int poseTextSize; 40 | const int numberTextSize; 41 | 42 | const unsigned char* faceColor; 43 | const unsigned char* foregroundTextColor; 44 | const unsigned char* pointColor; 45 | 46 | static const QByteArray FILE_IO_ERROR; 47 | static const QByteArray NO_FILE_ERROR; 48 | static const QByteArray IMAGE_TYPE_NOT_SUPPORTED; 49 | static const QByteArray NO_CHECKSUM; 50 | 51 | const static QByteArray JPEG; 52 | const static QByteArray JPG; 53 | }; 54 | 55 | #endif // GETFACERESOURCE_H 56 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHbbox.h: -------------------------------------------------------------------------------- 1 | /** @file bbox_t.h 2 | * @brief Multi-box bounding box type and operations 3 | * 4 | * A "bounding box" is defined as a collection of boxes, which serve as 5 | * detection results of different parts for part-based detection algorithms. 6 | * 7 | * @author Hang Su 8 | * @date 2012-08-13 9 | */ 10 | #ifndef bbox_t_H 11 | #define bbox_t_H 12 | 13 | #include "eHbox.h" 14 | 15 | #include 16 | #include 17 | 18 | /** @def EH_BBOXS_PRUNE 19 | * @brief Default pruning parameter for bbox_v_nms() 20 | * @sa bbox_v_nms() 21 | */ 22 | #define EH_BBOXS_PRUNE 30000 23 | 24 | /** @struct bbox_t 25 | * @brief Multi-box "bounding box", used for detection result 26 | */ 27 | struct bbox_t { 28 | std::vector boxes; /**< @brief part locations */ 29 | double score; /**< @brief detection score @warning not calibrated */ 30 | int component; /**< @brief component number for certain models */ 31 | fbox_t outer; /**< @brief outer "real" bounding box of the detection */ 32 | double area; /**< @brief area of outer */ 33 | int pose; /**< @brief the pose or direction of the face */ 34 | }; 35 | 36 | /** @brief Filling the fields of given bbox: outer, area 37 | */ 38 | void bbox_calcOut(bbox_t*); 39 | 40 | /** @brief Clip the boxes to image boundary 41 | * 42 | * @param imsize imsize[0]=height, imsize[1]=width 43 | * @sa fbox_clip() 44 | */ 45 | void bbox_clipboxes(bbox_t& bbox, const int* imsize); 46 | 47 | /** @brief Resize the input bboxs (in-place) 48 | * @sa fbox_resize() 49 | */ 50 | void bbox_v_resize(std::vector& bboxes, double scale); 51 | 52 | /** @brief Move the input bboxs (in-place) 53 | * @sa fbox_move() 54 | */ 55 | void bbox_v_move(std::vector& bboxes, const int* offset); 56 | 57 | /** @brief Non-maximum suppression 58 | * 59 | * Greedily select high-scoring detections and skip detections that are 60 | * significantly coverd by a previously selected detection. 61 | * 62 | * @param bboxes an array of detection results, changed inside function 63 | * @param overlap two results are not both kept if their overlap ratio exceed this value 64 | * @param prune initial bboxes are pruned for higher speed 65 | */ 66 | void bbox_v_nms(std::vector& bboxes, double overlap, unsigned prune = EH_BBOXS_PRUNE); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHmatrix.h: -------------------------------------------------------------------------------- 1 | /** @file eHmatrix.h 2 | * @brief Basic matrix types and operations 3 | * 4 | * @author Hang Su 5 | * @date 2012-07 6 | */ 7 | #ifndef EHMATRIX_H 8 | #define EHMATRIX_H 9 | 10 | #include 11 | 12 | /** @struct mat2d_t 13 | * @brief 2D matrix 14 | * @note matrix is stored in column-major style 15 | */ 16 | struct mat2d_t { 17 | double* vals; /**< @brief values */ 18 | size_t sizy; /**< @brief matrix height */ 19 | size_t sizx; /**< @brief matrix width */ 20 | }; 21 | 22 | /** @struct eHmatrix3d 23 | * @brief 3D matrix 24 | * @note matrix is stored in column-row-page order 25 | */ 26 | struct mat3d_t { 27 | double* vals; /**< @brief values */ 28 | size_t sizy; /**< @brief matrix height */ 29 | size_t sizx; /**< @brief matrix width */ 30 | size_t sizz; /**< @brief matrix depth */ 31 | }; 32 | 33 | /** @struct eHmatrixkd 34 | * @brief k-dimension matrix 35 | */ 36 | struct matkd_t { 37 | double* vals; /**< @brief values */ 38 | size_t k; /**< @brief number of dimensions */ 39 | size_t* siz; /**< @brief sizes along each dimension */ 40 | }; 41 | 42 | /** @brief Allocate a 2D matrix 43 | * @sa mat2d_delete() 44 | */ 45 | mat2d_t* mat2d_alloc(size_t sizy, size_t sizx); 46 | 47 | /** @brief Destruct a 2D matrix 48 | * @sa mat2d_alloc() 49 | */ 50 | void mat2d_delete(mat2d_t*); 51 | 52 | /** @brief Allocate a 3D matrix 53 | * @sa mat3d_delete() 54 | */ 55 | mat3d_t* mat3d_alloc(size_t sizy, size_t sizx, size_t sizz); 56 | 57 | /** @brief Destruct a 3D matrix 58 | * @sa mat3d_alloc() 59 | */ 60 | void mat3d_delete(mat3d_t*); 61 | 62 | /** @brief Pad 3d matrix 63 | * @param pad width of padding along each dimension 64 | * @param pad_val values to be padded in each dimension 65 | */ 66 | void mat3d_pad(mat3d_t* mat, const size_t* pad, double pad_val); 67 | 68 | /** @brief Fill continous region of a 3D matrix with fill_val 69 | * @param start starting point 70 | * @param width width in each dimension 71 | * @param fill_val the value to be filled in 72 | */ 73 | void mat3d_fill(mat3d_t* mat, const size_t* start, const size_t* width, double fill_val); 74 | 75 | /** @brief Allocate a kD matrix 76 | * @sa matkd_delete() 77 | */ 78 | matkd_t* matkd_alloc(size_t k, size_t* sizs); 79 | 80 | /** @brief Delete a kD matrix 81 | * @sa matkd_alloc() 82 | */ 83 | void matkd_delete(matkd_t*); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpconnectionhandlerpool.cpp: -------------------------------------------------------------------------------- 1 | #include "httpconnectionhandlerpool.h" 2 | 3 | HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler) 4 | : QObject() 5 | { 6 | Q_ASSERT(settings!=0); 7 | this->settings=settings; 8 | this->requestHandler=requestHandler; 9 | cleanupTimer.start(settings->value("cleanupInterval",1000).toInt()); 10 | connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); 11 | } 12 | 13 | 14 | HttpConnectionHandlerPool::~HttpConnectionHandlerPool() { 15 | // delete all connection handlers and wait until their threads are closed 16 | foreach(HttpConnectionHandler* handler, pool) { 17 | delete handler; 18 | } 19 | qDebug("HttpConnectionHandlerPool (%p): destroyed", this); 20 | } 21 | 22 | 23 | HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { 24 | HttpConnectionHandler* freeHandler=0; 25 | mutex.lock(); 26 | // find a free handler in pool 27 | foreach(HttpConnectionHandler* handler, pool) { 28 | if (!handler->isBusy()) { 29 | freeHandler=handler; 30 | freeHandler->setBusy(); 31 | break; 32 | } 33 | } 34 | // create a new handler, if necessary 35 | if (!freeHandler) { 36 | int maxConnectionHandlers=settings->value("maxThreads",100).toInt(); 37 | if (pool.count()setBusy(); 40 | pool.append(freeHandler); 41 | } 42 | } 43 | mutex.unlock(); 44 | return freeHandler; 45 | } 46 | 47 | 48 | 49 | void HttpConnectionHandlerPool::cleanup() { 50 | int maxIdleHandlers=settings->value("minThreads",1).toInt(); 51 | int idleCounter=0; 52 | mutex.lock(); 53 | foreach(HttpConnectionHandler* handler, pool) { 54 | if (!handler->isBusy()) { 55 | if (++idleCounter > maxIdleHandlers) { 56 | pool.removeOne(handler); 57 | delete handler; 58 | qDebug("HttpConnectionHandlerPool: Removed connection handler (%p), pool size is now %i",handler,pool.size()); 59 | break; // remove only one handler in each interval 60 | } 61 | } 62 | } 63 | mutex.unlock(); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/logmessage.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "logmessage.h" 7 | #include 8 | 9 | LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line) { 10 | this->type=type; 11 | this->message=message; 12 | this->file=file; 13 | this->function=function; 14 | this->line=line; 15 | timestamp=QDateTime::currentDateTime(); 16 | threadId=QThread::currentThreadId(); 17 | 18 | // Copy the logVars if not null, 19 | // so that later changes in the original do not affect the copy 20 | if (logVars) { 21 | this->logVars=*logVars; 22 | } 23 | } 24 | 25 | QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const { 26 | QString decorated=msgFormat+"\n"; 27 | decorated.replace("{msg}",message); 28 | 29 | if (decorated.contains("{timestamp}")) { 30 | decorated.replace("{timestamp}",timestamp.toString(timestampFormat)); 31 | } 32 | 33 | QString typeNr; 34 | typeNr.setNum(type); 35 | decorated.replace("{typeNr}",typeNr); 36 | 37 | switch (type) { 38 | case QtDebugMsg: 39 | decorated.replace("{type}","DEBUG"); 40 | break; 41 | case QtWarningMsg: 42 | decorated.replace("{type}","WARNING"); 43 | break; 44 | case QtCriticalMsg: 45 | decorated.replace("{type}","CRITICAL"); 46 | break; 47 | case QtFatalMsg: 48 | decorated.replace("{type}","FATAL"); 49 | break; 50 | default: 51 | decorated.replace("{type}",typeNr); 52 | } 53 | 54 | decorated.replace("{file}",file); 55 | decorated.replace("{function}",function); 56 | decorated.replace("{line}",QString::number(line)); 57 | 58 | QString threadId; 59 | threadId.setNum((unsigned long)QThread::currentThreadId()); 60 | decorated.replace("{thread}",threadId); 61 | 62 | // Fill in variables 63 | if (decorated.contains("{") && !logVars.isEmpty()) { 64 | QList keys=logVars.keys(); 65 | foreach (QString key, keys) { 66 | decorated.replace("{"+key+"}",logVars.value(key)); 67 | } 68 | } 69 | 70 | return decorated; 71 | } 72 | 73 | QtMsgType LogMessage::getType() const { 74 | return type; 75 | } 76 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpconnectionhandlerpool.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPCONNECTIONHANDLERPOOL_H 2 | #define HTTPCONNECTIONHANDLERPOOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "httpconnectionhandler.h" 9 | 10 | /** 11 | Pool of http connection handlers. Connection handlers are created on demand and idle handlers are 12 | cleaned up in regular time intervals. 13 |

14 | Example for the required configuration settings: 15 |

16 |   minThreads=1
17 |   maxThreads=100
18 |   cleanupInterval=1000
19 |   maxRequestSize=16000
20 |   maxMultiPartSize=1000000
21 |   
22 | The pool is empty initially and grows with the number of concurrent 23 | connections. A timer removes one idle connection handler at each 24 | interval, but it leaves some spare handlers in memory to improve 25 | performance. 26 | @see HttpConnectionHandler for description of config settings readTimeout 27 | @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize 28 | */ 29 | 30 | class HttpConnectionHandlerPool : public QObject { 31 | Q_OBJECT 32 | Q_DISABLE_COPY(HttpConnectionHandlerPool) 33 | public: 34 | 35 | /** 36 | Constructor. 37 | @param settings Configuration settings for the HTTP server. Must not be 0. 38 | @param requestHandler The handler that will process each received HTTP request. 39 | @warning The requestMapper gets deleted by the destructor of this pool 40 | */ 41 | HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler); 42 | 43 | /** Destructor */ 44 | virtual ~HttpConnectionHandlerPool(); 45 | 46 | /** Get a free connection handler, or 0 if not available. */ 47 | HttpConnectionHandler* getConnectionHandler(); 48 | 49 | private: 50 | 51 | /** Settings for this pool */ 52 | QSettings* settings; 53 | 54 | /** Will be assigned to each Connectionhandler during their creation */ 55 | HttpRequestHandler* requestHandler; 56 | 57 | /** Pool of connection handlers */ 58 | QList pool; 59 | 60 | /** Timer to clean-up unused connection handler */ 61 | QTimer cleanupTimer; 62 | 63 | /** Used to synchronize threads */ 64 | QMutex mutex; 65 | 66 | private slots: 67 | 68 | /** Received from the clean-up timer. */ 69 | void cleanup(); 70 | 71 | }; 72 | 73 | #endif // HTTPCONNECTIONHANDLERPOOL_H 74 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httplistener.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef LISTENER_H 7 | #define LISTENER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "httpconnectionhandler.h" 13 | #include "httpconnectionhandlerpool.h" 14 | #include "httprequesthandler.h" 15 | 16 | /** 17 | Listens for incoming TCP connections and and passes all incoming HTTP requests to your implementation of HttpRequestHandler, 18 | which processes the request and generates the response (usually a HTML document). 19 |

20 | Example for the required settings in the config file: 21 |

22 |   port=8080
23 |   minThreads=1
24 |   maxThreads=10
25 |   cleanupInterval=1000
26 |   readTimeout=60000
27 |   maxRequestSize=16000
28 |   maxMultiPartSize=1000000
29 |   
30 | The port number is the incoming TCP port that this listener listens to. 31 | @see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads and cleanupInterval 32 | @see HttpConnectionHandler for description of config settings readTimeout 33 | @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize 34 | */ 35 | 36 | class HttpListener : public QTcpServer { 37 | Q_OBJECT 38 | Q_DISABLE_COPY(HttpListener) 39 | public: 40 | 41 | /** 42 | Constructor. 43 | @param settings Configuration settings for the HTTP server. Must not be 0. 44 | @param requestHandler Processes each received HTTP request, usually by dispatching to controller classes. 45 | @param parent Parent object. 46 | */ 47 | HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = 0); 48 | 49 | /** Destructor */ 50 | virtual ~HttpListener(); 51 | 52 | protected: 53 | 54 | /** Serves new incoming connection requests */ 55 | void incomingConnection(tSocketDescriptor socketDescriptor); 56 | 57 | private: 58 | 59 | /** Configuration settings for the HTTP server */ 60 | QSettings* settings; 61 | 62 | /** Pool of connection handlers */ 63 | HttpConnectionHandlerPool* pool; 64 | 65 | signals: 66 | 67 | /** 68 | Emitted when the connection handler shall process a new incoming onnection. 69 | @param socketDescriptor references the accepted connection. 70 | */ 71 | 72 | void handleConnection(tSocketDescriptor socketDescriptor); 73 | 74 | }; 75 | 76 | #endif // LISTENER_H 77 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httplistener.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httplistener.h" 7 | #include "httpconnectionhandler.h" 8 | #include "httpconnectionhandlerpool.h" 9 | #include 10 | 11 | HttpListener::HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject *parent) 12 | : QTcpServer(parent) 13 | { 14 | Q_ASSERT(settings!=0); 15 | // Reqister type of socketDescriptor for signal/slot handling 16 | qRegisterMetaType("tSocketDescriptor"); 17 | // Create connection handler pool 18 | this->settings=settings; 19 | pool=new HttpConnectionHandlerPool(settings,requestHandler); 20 | // Start listening 21 | int port=settings->value("port").toInt(); 22 | listen(QHostAddress::Any, port); 23 | if (!isListening()) { 24 | qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); 25 | } 26 | else { 27 | qDebug("HttpListener: Listening on port %i",port); 28 | } 29 | } 30 | 31 | HttpListener::~HttpListener() { 32 | close(); 33 | qDebug("HttpListener: closed"); 34 | delete pool; 35 | qDebug("HttpListener: destroyed"); 36 | } 37 | 38 | void HttpListener::incomingConnection(tSocketDescriptor socketDescriptor) { 39 | #ifdef SUPERVERBOSE 40 | qDebug("HttpListener: New connection"); 41 | #endif 42 | HttpConnectionHandler* freeHandler=pool->getConnectionHandler(); 43 | 44 | // Let the handler process the new connection. 45 | if (freeHandler) { 46 | // The descriptor is passed via signal/slot because the handler lives in another 47 | // thread and cannot open the socket when called by another thread. 48 | connect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); 49 | emit handleConnection(socketDescriptor); 50 | disconnect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); 51 | } 52 | else { 53 | // Reject the connection 54 | qDebug("HttpListener: Too many incoming connections"); 55 | QTcpSocket* socket=new QTcpSocket(this); 56 | socket->setSocketDescriptor(socketDescriptor); 57 | connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); 58 | socket->write("HTTP/1.1 503 too many connections\r\nConnection: close\r\n\r\nToo many connections\r\n"); 59 | socket->disconnectFromHost(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHmatrix.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHmatrix.cpp 3 | * 4 | * Hang Su 5 | * 2012-07 @ eH 6 | */ 7 | #include "eHmatrix.h" 8 | 9 | mat2d_t* mat2d_alloc(size_t sizy, size_t sizx) { 10 | mat2d_t* mat = new mat2d_t; 11 | mat->vals = new double[sizy*sizx]; 12 | mat->sizy = sizy; 13 | mat->sizx = sizx; 14 | return mat; 15 | } 16 | 17 | void mat2d_delete(mat2d_t* mat) { 18 | delete[] mat->vals; 19 | delete mat; 20 | } 21 | 22 | mat3d_t* mat3d_alloc(size_t sizy, size_t sizx, size_t sizz) { 23 | mat3d_t* mat = new mat3d_t; 24 | mat->vals = new double[sizy*sizx*sizz]; 25 | mat->sizy = sizy; 26 | mat->sizx = sizx; 27 | mat->sizz = sizz; 28 | return mat; 29 | } 30 | 31 | void mat3d_delete(mat3d_t* mat) { 32 | delete[] mat->vals; 33 | delete mat; 34 | } 35 | 36 | void mat3d_pad(mat3d_t* mat, const size_t* pad, double pad_val) { 37 | size_t newy = mat->sizy+pad[0]*2; 38 | size_t newx = mat->sizx+pad[1]*2; 39 | size_t newz = mat->sizz+pad[2]*2; 40 | double* vals_new = new double[newy*newx*newz]; 41 | 42 | unsigned yy, xx, zz; 43 | bool pady, padx, padz; 44 | for(yy=0;yy=mat->sizy+pad[0]) 46 | pady=true; 47 | else 48 | pady=false; 49 | for(xx=0;xx=mat->sizx+pad[1]) 51 | padx=true; 52 | else 53 | padx=false; 54 | for(zz=0;zz=mat->sizz+pad[2]) 56 | padz=true; 57 | else 58 | padz=false; 59 | if (pady || padx || padz) 60 | vals_new[yy+xx*newy+zz*newy*newx] = pad_val; 61 | else 62 | vals_new[yy+xx*newy+zz*newy*newx] = 63 | mat->vals[yy-pad[0]+(xx-pad[1])*mat->sizy+(zz-pad[2])*mat->sizy*mat->sizx]; 64 | } 65 | } 66 | } 67 | 68 | delete[] mat->vals; 69 | mat->vals = vals_new; 70 | mat->sizy = newy; 71 | mat->sizx = newx; 72 | mat->sizz = newz; 73 | } 74 | 75 | void mat3d_fill(mat3d_t* mat, const size_t* start, const size_t* width, double fill_val){ 76 | unsigned yy, xx, zz; 77 | for (yy=start[0];yyvals[yy+xx*mat->sizy+zz*mat->sizy*mat->sizx] = fill_val; 81 | } 82 | 83 | matkd_t* matkd_alloc(size_t k, size_t* sizs) { 84 | matkd_t* mat = new matkd_t; 85 | mat->k = k; 86 | mat->siz = new size_t[k]; 87 | int len = 1; 88 | for (unsigned i=0;isiz[i]=sizs[i]; 91 | } 92 | mat->vals = new double[len]; 93 | return mat; 94 | } 95 | 96 | void matkd_delete(matkd_t* mat) { 97 | delete[] mat->siz; 98 | delete[] mat->vals; 99 | delete mat; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHbox.cpp: -------------------------------------------------------------------------------- 1 | #include "eHbox.h" 2 | #include "eHutils.h" 3 | 4 | #include "stdlib.h" 5 | 6 | #include 7 | 8 | #define EH_INF 1E20 9 | 10 | static inline double min(double x, double y) {return (x <= y ? x : y); } 11 | 12 | static inline double max(double x, double y) {return (x <= y ? y : x); } 13 | 14 | static inline int round2int(double x) { return (int)(x+0.5); } 15 | 16 | void fbox_set(fbox_t* box, double x1, double y1, double x2, double y2) { 17 | box->x1 = x1; 18 | box->y1 = y1; 19 | box->x2 = x2; 20 | box->y2 = y2; 21 | } 22 | 23 | ibox_t fbox_getibox(fbox_t* box) { 24 | ibox_t intbox; 25 | intbox.x1 = round2int(box->x1); 26 | intbox.x2 = round2int(box->x2); 27 | intbox.y1 = round2int(box->y1); 28 | intbox.y2 = round2int(box->y2); 29 | return intbox; 30 | } 31 | 32 | void ibox_set(ibox_t* box, int x1, int y1, int x2, int y2) { 33 | box->x1 = x1; 34 | box->y1 = y1; 35 | box->x2 = x2; 36 | box->y2 = y2; 37 | } 38 | 39 | double fbox_interArea(const fbox_t& box1, const fbox_t& box2) { 40 | double w = max(0,min(box1.x2,box2.x2)-max(box1.x1,box2.x1)); 41 | double h = max(0,min(box1.y2,box2.y2)-max(box1.y1,box2.y1)); 42 | return w*h; 43 | } 44 | 45 | void fbox_clip(fbox_t& box, const int* imsize) { 46 | box.x1 = max(0, box.x1); 47 | box.y1 = max(0, box.y1); 48 | box.x2 = min(imsize[1]-1, box.x2); 49 | box.y2 = min(imsize[0]-1, box.y2); 50 | } 51 | 52 | fbox_t fbox_merge(const vector& boxes, const int* idxs, int num, const double* padding) { 53 | double tmp_x1=EH_INF, tmp_x2=-EH_INF, tmp_y1=EH_INF, tmp_y2=-EH_INF; 54 | for (int i=0;ix1*scale, box->y1*scale, box->x2*scale, box->y2*scale); 86 | } 87 | 88 | void fbox_move(fbox_t* box, const int* offset) { 89 | fbox_set(box, box->x1+offset[1], box->y1+offset[0], box->x2+offset[1], box->y2+offset[0]); 90 | } 91 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/staticfilecontroller.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef STATICFILECONTROLLER_H 7 | #define STATICFILECONTROLLER_H 8 | 9 | #include "httprequest.h" 10 | #include "httpresponse.h" 11 | #include "httprequesthandler.h" 12 | #include 13 | #include 14 | 15 | /** 16 | Delivers static files. It is usually called by the applications main request handler when 17 | the caller request a path that is mapped to static files. 18 |

19 | The following settings are required in the config file: 20 |

21 |   path=docroot
22 |   encoding=UTF-8
23 |   maxAge=60000
24 |   cacheTime=60000
25 |   cacheSize=1000000
26 |   maxCachedFileSize=65536
27 |   
28 | The path is relative to the directory of the config file. In case of windows, if the 29 | settings are in the registry, the path is relative to the current working directory. 30 |

31 | The encoding is sent to the web browser in case of text and html files. 32 |

33 | The cache improves performance of small files when loaded from a network 34 | drive. Large files are not cached. Files are cached as long as possible, 35 | when cacheTime=0. The maxAge value (in msec!) controls the remote browsers cache. 36 |

37 | Do not instantiate this class in each request, because this would make the file cache 38 | useless. Better create one instance during start-up and call it when the application 39 | received a related HTTP request. 40 | */ 41 | 42 | class StaticFileController : public HttpRequestHandler { 43 | Q_OBJECT 44 | Q_DISABLE_COPY(StaticFileController); 45 | public: 46 | 47 | /** Constructor */ 48 | StaticFileController(QSettings* settings, QObject* parent = 0); 49 | 50 | /** Generates the response */ 51 | void service(HttpRequest& request, HttpResponse& response); 52 | 53 | private: 54 | 55 | /** Encoding of text files */ 56 | QString encoding; 57 | 58 | /** Root directory of documents */ 59 | QString docroot; 60 | 61 | /** Maximum age of files in the browser cache */ 62 | int maxAge; 63 | 64 | struct CacheEntry { 65 | QByteArray document; 66 | qint64 created; 67 | }; 68 | 69 | /** Timeout for each cached file */ 70 | int cacheTimeout; 71 | 72 | /** Maximum size of files in cache, larger files are not cached */ 73 | int maxCachedFileSize; 74 | 75 | /** Cache storage */ 76 | QCache cache; 77 | 78 | /** Used to synchronize cache access for threads */ 79 | QMutex mutex; 80 | 81 | /** Set a content-type header in the response depending on the ending of the filename */ 82 | void setContentType(QString file, HttpResponse& response) const; 83 | }; 84 | 85 | #endif // STATICFILECONTROLLER_H 86 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHbox.h: -------------------------------------------------------------------------------- 1 | /** @file eHbox.h 2 | * @brief Box types and operations 3 | * 4 | * Boxes are defined as tuples (x1, y1, x2, y2), where 5 | * x1<=x2, y1<=y2; so (x1,y1) is the top-left corner, 6 | * (x2,y2) is the bottom-right corner. 7 | * 8 | * @author Hang Su 9 | * @date 2012-08-13 10 | */ 11 | #ifndef EHBOX_H 12 | #define EHBOX_H 13 | 14 | #include 15 | #include 16 | using std::vector; 17 | 18 | /** @struct fbox_t 19 | * @brief box with floating-point boundaries 20 | * 21 | * Boxes are defined as tuples (x1, y1, x2, y2), where 22 | * x1<=x2, y1<=y2; so (x1,y1) is the top-left corner, 23 | * (x2,y2) is the bottom-right corner. 24 | */ 25 | struct fbox_t { 26 | double x1; /**< @brief left bound */ 27 | double y1; /**< @brief up bound */ 28 | double x2; /**< @brief right bound */ 29 | double y2; /**< @brief bottom bound */ 30 | }; 31 | 32 | /** @struct ibox_t 33 | * @brief box with integer boundaries 34 | * 35 | * Boxes are defined as tuples (x1, y1, x2, y2), where 36 | * x1<=x2, y1<=y2; so (x1,y1) is the top-left corner, 37 | * (x2,y2) is the bottom-right corner. 38 | */ 39 | struct ibox_t { 40 | int x1; /**< @brief left bound */ 41 | int y1; /**< @brief up bound */ 42 | int x2; /**< @brief right bound */ 43 | int y2; /**< @brief bottom bound */ 44 | }; 45 | 46 | /** @brief Set values for given box 47 | */ 48 | void fbox_set(fbox_t* box, double x1, double y1, double x2, double y2); 49 | 50 | /** @brief Round floating-point box to intege box 51 | */ 52 | ibox_t fbox_getibox(fbox_t* box); 53 | 54 | /** @brief Set values for given box 55 | */ 56 | void ibox_set(ibox_t*, int x1, int y1, int x2, int y2); 57 | 58 | /** @brief Compute area of intersection between two boxes 59 | */ 60 | double fbox_interArea(const fbox_t& box1, const fbox_t& box2); 61 | 62 | /** @brief Clip box so it's limited by the image size 63 | */ 64 | void fbox_clip(fbox_t& box, const int* imsize); 65 | 66 | /** @brief Merge boxes, resulting box can be optionally padded 67 | * 68 | * @param boxes array of boxes to be merged 69 | * @param idxs indexs within vector, indicating which boxes to merge 70 | * @param num length of idxs 71 | * @param padding [l r u d] as ratio of corresponding edge, if NULL is passed in, 72 | * no padding is performed 73 | * @return merged box 74 | */ 75 | fbox_t fbox_merge(const vector& boxes, const int* idxs, int num, const double* padding=NULL); 76 | 77 | /** @brief Get a resized box, input box is not changed 78 | * @sa fbox_resize() 79 | */ 80 | fbox_t fbox_getResized(const fbox_t& box, double scale); 81 | 82 | /** @brief Get a moved box, input box is not changed 83 | * @sa fbox_move() 84 | */ 85 | fbox_t fbox_getMoved(const fbox_t& box, const int* offset); 86 | 87 | /** @brief Resize the input box 88 | * @sa fbox_getResized() 89 | */ 90 | void fbox_resize(fbox_t* box, double scale); 91 | 92 | /** @brief Move the position of input box by given offset 93 | * @sa fbox_getMoved() 94 | */ 95 | void fbox_move(fbox_t* box, const int* offset); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHposemodel.h: -------------------------------------------------------------------------------- 1 | /** @file eHposemodel.h 2 | * @brief Human body/pose detection model and operations 3 | * @sa Y. Yang, D. Ramanan, "Articulated Pose Estimation using Flexible Mixtures of Parts". In CVPR 2011. 4 | * 5 | * @author Hang Su 6 | * @date 2012-08 7 | */ 8 | #ifndef EHPOSEMODEL_H 9 | #define EHPOSEMODEL_H 10 | 11 | #include 12 | #include "eHimage.h" 13 | #include "eHfilter.h" 14 | #include "eHbbox.h" 15 | 16 | using std::vector; 17 | 18 | typedef struct deformation_pose { 19 | int i; 20 | int anchor[3]; 21 | double w[4]; 22 | } posedef_t; 23 | 24 | typedef struct part_pose { 25 | int* biasid; /*numpar x num (except for root, which is 1)*/ 26 | int* defid; /*num (except for root, which is NULL)*/ 27 | int* filterid; /*num*/ 28 | int num; 29 | int numpar; 30 | int parent; 31 | /* infos not directly from file */ 32 | int* sizy; /*num*/ 33 | int* sizx;/*num*/ 34 | int scale; 35 | int step; 36 | int* startx;/*num*/ 37 | int* starty;/*num*/ 38 | } posepart_t; 39 | 40 | typedef struct bias_pose { 41 | int i; 42 | double w; 43 | } posebias_t; 44 | 45 | /** @struct posemodel_t 46 | * @brief Human body/pose mdoel 47 | */ 48 | struct posemodel_t { 49 | vector biases; /**< @brief bias towards part combinations */ 50 | vector filters; /**< @brief part filters @note all filters should be of the same size */ 51 | vector defs; /**< @brief deformation params */ 52 | vector parts; /**< @brief part configurations */ 53 | int maxsize[2]; /**< @brief XXX */ 54 | int len; /**< @brief not used */ 55 | int interval; /**< @brief interval of feature pyramid */ 56 | int sbin; /**< @brief bin for building hog feature */ 57 | double thresh; /**< @brief threshold for detection score */ 58 | double obj; /**< @brief not used */ 59 | }; 60 | 61 | /** @brief Parse body/pose model from xml style string 62 | * @note xmlstr is modified during parsing, this can be avoided by 63 | * using Non-Destrutive Mode of rapidxml 64 | */ 65 | posemodel_t* posemodel_parseXml(char* xmlstr); 66 | 67 | /** @brief Read body/pose model from file 68 | * @sa posemodel_parseXml() 69 | */ 70 | posemodel_t* posemodel_readFromFile(const char* filepath); 71 | 72 | /** @brief Perform body/pose detection 73 | * @param model pose detection model 74 | * @param img where to find human poses from 75 | * @param thrs threshold used for pruning results 76 | * @return array of detected poses (together with part locations) 77 | */ 78 | vector posemodel_detect(const posemodel_t* model, const image_t* img, double thrs); 79 | 80 | /** @brief Perform body/pose detection using default threshold 81 | * @param model pose detection model 82 | * @param img where to find human poses from 83 | * @return array of detected poses (together with part locations) 84 | */ 85 | vector posemodel_detect(const posemodel_t* model, const image_t* img); 86 | 87 | /** @brief Delete a pose model, release related memory 88 | */ 89 | void posemodel_delete(posemodel_t* model); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHbbox.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * bbox_t.cpp 3 | * 4 | * Hang Su 5 | * 2012-08 @ eH 6 | */ 7 | #include "eHbbox.h" 8 | #include 9 | #include 10 | #include 11 | 12 | #define EH_INF 1E20 13 | 14 | using std::vector; 15 | using std::list; 16 | 17 | static inline double min(double x, double y) {return (x <= y ? x : y); } 18 | static inline double max(double x, double y) {return (x <= y ? y : x); } 19 | 20 | /* 21 | * filling the fields of given bbox: 22 | * outer - bounding box covering all parts 23 | * area - area of outer 24 | */ 25 | void bbox_calcOut(bbox_t* bbox){ 26 | vector::iterator iter; 27 | fbox_set(&(bbox->outer),EH_INF,EH_INF,-EH_INF,-EH_INF); 28 | for(iter=bbox->boxes.begin();iter!=bbox->boxes.end();iter++) 29 | fbox_set(&(bbox->outer), 30 | min(bbox->outer.x1,iter->x1), 31 | min(bbox->outer.y1,iter->y1), 32 | max(bbox->outer.x2,iter->x2), 33 | max(bbox->outer.y2,iter->y2) 34 | ); 35 | bbox->area = max(0,(bbox->outer.x2-bbox->outer.x1)) 36 | *max(0,(bbox->outer.y2-bbox->outer.y1)); 37 | } 38 | 39 | void bbox_clipboxes(bbox_t& bbox, const int* imsize) { 40 | for(unsigned i = 0;i& bboxes, double scale) { 46 | for(unsigned i=0;i& bboxes, const int* offset) { 54 | for(unsigned i=0;ib.score);} 61 | 62 | void bbox_v_nms(vector& bboxes, double overlap, unsigned prune) { 63 | if (bboxes.empty()) 64 | return ; 65 | /* sort bboxes according to score */ 66 | sort(bboxes.begin(), bboxes.end(), compare_score); 67 | 68 | /* throw away boxes with low score if there are too many */ 69 | if(bboxes.size()>prune) 70 | bboxes.resize(prune); 71 | 72 | /* get bounding box & area */ 73 | vector::iterator iter; 74 | for(iter=bboxes.begin();iter!=bboxes.end();iter++) 75 | bbox_calcOut(&(*iter)); 76 | 77 | /* use list for frequent removal */ 78 | list lsbboxes(bboxes.begin(),bboxes.end()); 79 | list::iterator iter_l1, iter_l2, iter_tmp; 80 | double intersect; 81 | iter_l1 = lsbboxes.begin(); 82 | while(iter_l1!=lsbboxes.end()){ 83 | iter_l2 = iter_l1; 84 | iter_l2++; 85 | while(iter_l2!=lsbboxes.end()){ 86 | intersect = fbox_interArea(iter_l1->outer, iter_l2->outer); 87 | if((intersect/iter_l1->area)>overlap || (intersect/iter_l2->area)>overlap) { 88 | iter_tmp = iter_l2; 89 | iter_tmp++; 90 | lsbboxes.erase(iter_l2); 91 | iter_l2 = iter_tmp; 92 | continue; 93 | } 94 | iter_l2++; 95 | } 96 | iter_l1++; 97 | } 98 | /* copy results back to vector*/ 99 | bboxes.resize(lsbboxes.size()); 100 | copy(lsbboxes.begin(),lsbboxes.end(),bboxes.begin()); 101 | } 102 | 103 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpconnectionhandler.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPCONNECTIONHANDLER_H 7 | #define HTTPCONNECTIONHANDLER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "httprequest.h" 14 | #include "httprequesthandler.h" 15 | 16 | /** 17 | The connection handler accepts incoming connections and dispatches incoming requests to to a 18 | request mapper. Since HTTP clients can send multiple requests before waiting for the response, 19 | the incoming requests are queued and processed one after the other. 20 |

21 | Example for the required configuration settings: 22 |

 23 |   readTimeout=60000
 24 |   maxRequestSize=16000
 25 |   maxMultiPartSize=1000000
 26 |   
27 |

28 | The readTimeout value defines the maximum time to wait for a complete HTTP request. 29 | @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize 30 | */ 31 | 32 | #if QT_VERSION >= 0x050000 33 | typedef qintptr tSocketDescriptor; 34 | #else 35 | typedef int tSocketDescriptor; 36 | #endif 37 | 38 | class HttpConnectionHandler : public QThread { 39 | Q_OBJECT 40 | Q_DISABLE_COPY(HttpConnectionHandler) 41 | public: 42 | 43 | /** 44 | Constructor. 45 | @param settings Configuration settings of the HTTP webserver 46 | @param requestHandler handler that will process each incomin HTTP request 47 | */ 48 | HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler); 49 | 50 | /** Destructor */ 51 | virtual ~HttpConnectionHandler(); 52 | 53 | /** Returns true, if this handler is in use. */ 54 | bool isBusy(); 55 | 56 | /** Mark this handler as busy */ 57 | void setBusy(); 58 | 59 | private: 60 | 61 | /** Configuration settings */ 62 | QSettings* settings; 63 | 64 | /** TCP socket of the current connection */ 65 | QTcpSocket socket; 66 | 67 | /** Time for read timeout detection */ 68 | QTimer readTimer; 69 | 70 | /** Storage for the current incoming HTTP request */ 71 | HttpRequest* currentRequest; 72 | 73 | /** Dispatches received requests to services */ 74 | HttpRequestHandler* requestHandler; 75 | 76 | /** This shows the busy-state from a very early time */ 77 | bool busy; 78 | 79 | /** Executes the threads own event loop */ 80 | void run(); 81 | 82 | public slots: 83 | 84 | /** 85 | Received from from the listener, when the handler shall start processing a new connection. 86 | @param socketDescriptor references the accepted connection. 87 | */ 88 | void handleConnection(tSocketDescriptor socketDescriptor); 89 | 90 | private slots: 91 | 92 | /** Received from the socket when a read-timeout occured */ 93 | void readTimeout(); 94 | 95 | /** Received from the socket when incoming data can be read */ 96 | void read(); 97 | 98 | /** Received from the socket when a connection has been closed */ 99 | void disconnected(); 100 | 101 | }; 102 | 103 | #endif // HTTPCONNECTIONHANDLER_H 104 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/logmessage.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef LOGMESSAGE_H 7 | #define LOGMESSAGE_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /** 14 | Represents a single log message together with some data 15 | that are used to decorate the log message. 16 | 17 | The following variables may be used in the message and in msgFormat: 18 | 19 | - {timestamp} Date and time of creation 20 | - {typeNr} Type of the message in numeric format (0-3) 21 | - {type} Type of the message in string format (DEBUG, WARNING, CRITICAL, FATAL) 22 | - {thread} ID number of the thread 23 | - {msg} Message text (only useable in msgFormat) 24 | - {file} Filename where the message was generated # 25 | - {function} Function where the message was generated # 26 | - {line} Line number where the message was generated # 27 | - {xxx} For any user-defined logger variable 28 | 29 | # The macros qDebug()...qFatal() dont fill these variable in case of QT versions before 5.0. 30 | */ 31 | 32 | class LogMessage 33 | { 34 | Q_DISABLE_COPY(LogMessage) 35 | public: 36 | 37 | /** 38 | Constructor. All parameters are copied, so that later changes to them do not 39 | affect this object. 40 | @param type Type of the message 41 | @param message Message text 42 | @param logVars Logger variables, 0 is allowed 43 | @param file Name of the source file where the message was generated 44 | @param function Name of the function where the message was generated 45 | @param line Line Number of the source file, where the message was generated 46 | */ 47 | LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line); 48 | 49 | /** 50 | Returns the log message as decorated string. 51 | @param msgFormat Format of the decoration. May contain variables and static text, 52 | e.g. "{timestamp} {type} thread={thread}: {msg}". 53 | @param timestampFormat Format of timestamp, e.g. "dd.MM.yyyy hh:mm:ss.zzz", see QDateTime::toString(). 54 | @see QDatetime for a description of the timestamp format pattern 55 | */ 56 | QString toString(const QString& msgFormat, const QString& timestampFormat) const; 57 | 58 | /** 59 | Get the message type. 60 | */ 61 | QtMsgType getType() const; 62 | 63 | private: 64 | 65 | /** Logger variables */ 66 | QHash logVars; 67 | 68 | /** Date and time of creation */ 69 | QDateTime timestamp; 70 | 71 | /** Type of the message */ 72 | QtMsgType type; 73 | 74 | /** ID number of the thread */ 75 | Qt::HANDLE threadId; 76 | 77 | /** Message text */ 78 | QString message; 79 | 80 | /** Filename where the message was generated */ 81 | QString file; 82 | 83 | /** Function name where the message was generated */ 84 | QString function; 85 | 86 | /** Line number where the message was generated */ 87 | int line; 88 | 89 | }; 90 | 91 | #endif // LOGMESSAGE_H 92 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHfacemodel.h: -------------------------------------------------------------------------------- 1 | /** @file eHfacemodel.h 2 | * @brief face detection model and operations 3 | * 4 | * @sa Xiangzin Zhu, Deva Ramanan, "Face Detection, Pose Estimation, and landmark Localization in the Wild". In CVPR 2012. 5 | * 6 | * @author Hang Su 7 | * @date 2012-08-13 8 | */ 9 | #ifndef EHFACEMODEL_H 10 | #define EHFACEMODEL_H 11 | 12 | #include 13 | #include "eHimage.h" 14 | #include "eHfilter.h" 15 | #include "eHbbox.h" 16 | #include "eHposemodel.h" 17 | 18 | //#define EH_TEST_TIMER 19 | 20 | using std::vector; 21 | 22 | typedef struct deformation_face { 23 | int i; 24 | int anchor[3]; /*ax ay ds*/ 25 | double w[4]; /*only 1st is used for root*/ 26 | } facedef_t; 27 | 28 | typedef struct part_face { 29 | int defid; 30 | int filterid; 31 | int parent; 32 | /* infos not directly from file */ 33 | int sizy; 34 | int sizx; 35 | int scale; 36 | int startx; 37 | int starty; 38 | int step; 39 | } facepart_t; 40 | 41 | /** @struct facemodel_t 42 | * @brief Face detection model 43 | */ 44 | struct facemodel_t { 45 | vector filters; /**< @brief part filters 46 | @note All part filters should be 47 | of the same size*/ 48 | vector defs; /**< @brief deformation params */ 49 | vector > components; /**< @brief part infos */ 50 | int maxsize[2]; /**< @brief XXX */ 51 | int len; /**< @brief not used */ 52 | int interval; /**< @brief interval of pyramid */ 53 | int sbin; /**< @brief bin for building hog feature */ 54 | double delta; /**< @brief not used */ 55 | double thresh; /**< @brief threshold for detection score */ 56 | double obj; /**< @brief not used */ 57 | vector posemap; /**< @brief the possible poses for the face, to find the pose use the number of components in the part result */ 58 | }; 59 | 60 | /** @brief Parse face model from xml style string 61 | * @note xmlstr is modified during parsing, this can be avoided by 62 | * using Non-Destrutive Mode of rapidxml 63 | */ 64 | facemodel_t* facemodel_parseXml(char* xmlstr); 65 | 66 | /** @brief Read face model from file 67 | * @sa facemodel_parseXml() 68 | */ 69 | facemodel_t* facemodel_readFromFile(const char* filepath); 70 | 71 | /** @brief Perform face detection 72 | * @param model face detection model 73 | * @param img where to find faces from 74 | * @param thrs threshold used for pruning results 75 | * @return array of detected faces (together with part locations) 76 | */ 77 | vector facemodel_detect(const facemodel_t* model, const image_t* img, double thrs); 78 | 79 | /** @brief Perform face detection using threshold inside model 80 | * @param model face detection model 81 | * @param img where to find faces from 82 | * @return array of detected faces (together with part locations) 83 | */ 84 | vector facemodel_detect(const facemodel_t* model, const image_t* img); 85 | 86 | /** @brief Perform face detection with help of body detection using 87 | * thresholds inside models 88 | * @param facemodel face detection model 89 | * @param posemodel body pose detection model 90 | * @param img where to find faces from 91 | * @return array of detected faces (together with part locations) 92 | */ 93 | vector facemodel_detect(const facemodel_t* facemodel, const posemodel_t* posemodel, const image_t* img); 94 | 95 | /** @brief Delete a face model, release related memory 96 | */ 97 | void facemodel_delete(facemodel_t* model); 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHfeatpyramid.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHfeatpyramid.cpp 3 | * 4 | * Hang Su 5 | * 2012-08 @ eH 6 | */ 7 | #include "eHfeatpyramid.h" 8 | #include "eHimageFeature.h" 9 | 10 | #include 11 | 12 | static inline int min(int x, int y) { return (x <= y ? x : y); } 13 | static inline int max(int x, int y) { return (x <= y ? y : x); } 14 | 15 | featpyra_t* featpyra_create(const image_t* im, int interval, int sbin, const int* maxsize, bool hallucinate) { 16 | featpyra_t* pyra = new featpyra_t; 17 | 18 | /* select padding, allowing for one cell in model to be visible 19 | * Even padding allows for consistent spatial relations across 2X scales */ 20 | int pady = max(maxsize[0]-1-1,0); 21 | int padx = max(maxsize[1]-1-1,0); 22 | double sc = pow(2.0, 1.0/(double)interval); 23 | 24 | int min_level = floor(log((double)min(im->sizy,im->sizx)/(5.0*sbin))/log(sc)); 25 | image_t* scaled; 26 | image_t* tmp; 27 | pyra->len = min_level+1; 28 | if(hallucinate) pyra->len += interval; 29 | pyra->feat = new mat3d_t*[pyra->len]; 30 | pyra->scale = new double[pyra->len]; 31 | for(int i=0;ifeat[i]=eHhog(scaled, sbin/2); 36 | pyra->scale[i]=2.0/pow(sc,i); 37 | pyra->feat[i+interval] = eHhog(scaled,sbin); 38 | pyra->scale[i+interval] = 1.0/pow(sc,i); 39 | } else { 40 | pyra->feat[i] = eHhog(scaled,sbin); 41 | pyra->scale[i] = 1.0/pow(sc,i); 42 | } 43 | 44 | /* remaining octaves */ 45 | for (int j=i+interval;; j+=interval){ 46 | tmp = image_reduce(scaled); 47 | image_delete(scaled); 48 | scaled = tmp; 49 | if(hallucinate) { 50 | pyra->feat[j+interval] = eHhog(scaled,sbin); 51 | pyra->scale[j+interval] = 0.5*pyra->scale[j]; 52 | if(j+interval+interval>=pyra->len) 53 | break; 54 | } else { 55 | pyra->feat[j] = eHhog(scaled, sbin); 56 | pyra->scale[j] = 0.5*pyra->scale[j-interval]; 57 | if(j+interval>=pyra->len) 58 | break; 59 | } 60 | } 61 | image_delete(scaled); 62 | } 63 | 64 | size_t pad[] = {pady+1, padx+1, 0}; 65 | size_t fill_start[3]; 66 | size_t fill_width[3]; 67 | for (int i=0;ilen;i++) { 68 | /* add 1 to padding because feature generation deletes a 1-cell 69 | * wide border around the feature map */ 70 | mat3d_pad(pyra->feat[i],pad,0); 71 | /* write boundary occlusion feature */ 72 | fill_start[2]=pyra->feat[i]->sizz-1;fill_width[2]=1; 73 | fill_start[0]=0;fill_width[0]=pady+1; 74 | fill_start[1]=0;fill_width[1]=pyra->feat[i]->sizx; 75 | mat3d_fill(pyra->feat[i],fill_start,fill_width,1); 76 | fill_start[0]=pyra->feat[i]->sizy-pady-1; 77 | mat3d_fill(pyra->feat[i],fill_start,fill_width,1); 78 | fill_start[0]=pady+1;fill_width[0]=pyra->feat[i]->sizy-pady*2-2; 79 | fill_start[1]=0;fill_width[1]=padx+1; 80 | mat3d_fill(pyra->feat[i],fill_start,fill_width,1); 81 | fill_start[1]=pyra->feat[i]->sizx-padx-1; 82 | mat3d_fill(pyra->feat[i],fill_start,fill_width,1); 83 | } 84 | 85 | for (int i=0;ilen;i++) 86 | pyra->scale[i] = (double)sbin/pyra->scale[i]; 87 | pyra->interval = interval; 88 | pyra->imy = im->sizy; 89 | pyra->imx = im->sizx; 90 | 91 | return pyra; 92 | } 93 | 94 | void featpyra_delete(featpyra_t* pyra) { 95 | if(pyra==NULL) return; 96 | for (int i=0;ilen;i++) { 97 | mat3d_delete(pyra->feat[i]); 98 | } 99 | delete[] pyra->feat; 100 | delete[] pyra->scale; 101 | delete pyra; 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpsessionstore.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPSESSIONSTORE_H 7 | #define HTTPSESSIONSTORE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "httpsession.h" 14 | #include "httpresponse.h" 15 | #include "httprequest.h" 16 | 17 | /** 18 | Stores HTTP sessions and deletes them when they have expired. 19 | The following configuration settings are required in the config file: 20 |

 21 |   expirationTime=3600000
 22 |   cookieName=sessionid
 23 |   
24 | The following additional configurations settings are optionally: 25 |
 26 |   cookiePath=/
 27 |   cookieComment=Session ID
 28 |   cookieDomain=stefanfrings.de
 29 |   
30 | */ 31 | 32 | class HttpSessionStore : public QObject { 33 | Q_OBJECT 34 | Q_DISABLE_COPY(HttpSessionStore) 35 | public: 36 | 37 | /** Constructor. */ 38 | HttpSessionStore(QSettings* settings, QObject* parent); 39 | 40 | /** Destructor */ 41 | virtual ~HttpSessionStore(); 42 | 43 | /** 44 | Get the ID of the current HTTP session, if it is valid. 45 | This method is thread safe. 46 | @warning Sessions may expire at any time, so subsequent calls of 47 | getSession() might return a new session with a different ID. 48 | @param request Used to get the session cookie 49 | @param response Used to get and set the new session cookie 50 | @return Empty string, if there is no valid session. 51 | */ 52 | QByteArray getSessionId(HttpRequest& request, HttpResponse& response); 53 | 54 | /** 55 | Get the session of a HTTP request, eventually create a new one. 56 | This method is thread safe. New sessions can only be created before 57 | the first byte has been written to the HTTP response. 58 | @param request Used to get the session cookie 59 | @param response Used to get and set the new session cookie 60 | @param allowCreate can be set to false, to disable the automatic creation of a new session. 61 | @return If autoCreate is disabled, the function returns a null session if there is no session. 62 | @see HttpSession::isNull() 63 | */ 64 | HttpSession getSession(HttpRequest& request, HttpResponse& response, bool allowCreate=true); 65 | 66 | /** 67 | Get a HTTP session by it's ID number. 68 | This method is thread safe. 69 | @return If there is no such session, the function returns a null session. 70 | @param id ID number of the session 71 | @see HttpSession::isNull() 72 | */ 73 | HttpSession getSession(const QByteArray id); 74 | 75 | /** Delete a session */ 76 | void removeSession(HttpSession session); 77 | 78 | private: 79 | 80 | /** Configuration settings */ 81 | QSettings* settings; 82 | 83 | /** Storage for the sessions */ 84 | QMap sessions; 85 | 86 | /** Timer to remove expired sessions */ 87 | QTimer cleanupTimer; 88 | 89 | /** Name of the session cookie */ 90 | QByteArray cookieName; 91 | 92 | /** Time when sessions expire (in ms)*/ 93 | int expirationTime; 94 | 95 | /** Used to synchronize threads */ 96 | QMutex mutex; 97 | 98 | using QObject::timerEvent; 99 | 100 | private slots: 101 | /** Called every minute to cleanup expired sessions. */ 102 | void timerEvent(); 103 | }; 104 | 105 | #endif // HTTPSESSIONSTORE_H 106 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpsession.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPSESSION_H 7 | #define HTTPSESSION_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | /** 15 | This class stores data for a single HTTP session. 16 | A session can store any number of key/value pairs. This class uses implicit 17 | sharing for read and write access. This class is thread safe. 18 | @see HttpSessionStore should be used to create and get instances of this class. 19 | */ 20 | 21 | class HttpSession { 22 | 23 | public: 24 | 25 | /** 26 | Constructor. 27 | @param canStore The session can store data, if this parameter is true. 28 | Otherwise all calls to set() and remove() do not have any effect. 29 | */ 30 | HttpSession(bool canStore=false); 31 | 32 | /** 33 | Copy constructor. Creates another HttpSession object that shares the 34 | data of the other object. 35 | */ 36 | HttpSession(const HttpSession& other); 37 | 38 | /** 39 | Copy operator. Detaches from the current shared data and attaches to 40 | the data of the other object. 41 | */ 42 | HttpSession& operator= (const HttpSession& other); 43 | 44 | 45 | /** 46 | Destructor. Detaches from the shared data. 47 | */ 48 | virtual ~HttpSession(); 49 | 50 | /** Get the unique ID of this session. This method is thread safe. */ 51 | QByteArray getId() const; 52 | 53 | /** 54 | Null sessions cannot store data. All calls to set() and remove() 55 | do not have any effect.This method is thread safe. 56 | */ 57 | bool isNull() const; 58 | 59 | /** Set a value. This method is thread safe. */ 60 | void set(const QByteArray& key, const QVariant& value); 61 | 62 | /** Remove a value. This method is thread safe. */ 63 | void remove(const QByteArray& key); 64 | 65 | /** Get a value. This method is thread safe. */ 66 | QVariant get(const QByteArray& key) const; 67 | 68 | /** Check if a key exists. This method is thread safe. */ 69 | bool contains(const QByteArray& key) const; 70 | 71 | /** 72 | Get a copy of all data stored in this session. 73 | Changes to the session do not affect the copy and vice versa. 74 | This method is thread safe. 75 | */ 76 | QMap getAll() const; 77 | 78 | /** 79 | Get the timestamp of last access. That is the time when the last 80 | HttpSessionStore::getSession() has been called. 81 | This method is thread safe. 82 | */ 83 | qint64 getLastAccess() const; 84 | 85 | /** 86 | Set the timestamp of last access, to renew the timeout period. 87 | Called by HttpSessionStore::getSession(). 88 | This method is thread safe. 89 | */ 90 | void setLastAccess(); 91 | 92 | private: 93 | 94 | struct HttpSessionData { 95 | 96 | /** Unique ID */ 97 | QByteArray id; 98 | 99 | /** Timestamp of last access, set by the HttpSessionStore */ 100 | qint64 lastAccess; 101 | 102 | /** Reference counter */ 103 | int refCount; 104 | 105 | /** Used to synchronize threads */ 106 | QReadWriteLock lock; 107 | 108 | /** Storage for the key/value pairs; */ 109 | QMap values; 110 | 111 | }; 112 | 113 | /** Pointer to the shared data. */ 114 | HttpSessionData* dataPtr; 115 | 116 | }; 117 | 118 | #endif // HTTPSESSION_H 119 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpcookie.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPCOOKIE_H 7 | #define HTTPCOOKIE_H 8 | 9 | #include 10 | #include 11 | 12 | /** 13 | HTTP cookie as defined in RFC 2109. This class can also parse 14 | RFC 2965 cookies, but skips fields that are not defined in RFC 15 | 2109. 16 | */ 17 | 18 | class HttpCookie 19 | { 20 | public: 21 | 22 | /** Creates an empty cookie */ 23 | HttpCookie(); 24 | 25 | /** 26 | Create a cookie and set name/value pair. 27 | @param name name of the cookie 28 | @param value value of the cookie 29 | @param maxAge maximum age of the cookie in seconds. 0=discard immediately 30 | @param path Path for that the cookie will be sent, default="/" which means the whole domain 31 | @param comment Optional comment, may be displayed by the web browser somewhere 32 | @param domain Optional domain for that the cookie will be sent. Defaults to the current domain 33 | @param secure If true, the cookie will only be sent on secure connections 34 | */ 35 | HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false); 36 | 37 | /** 38 | Create a cookie from a string. 39 | @param source String as received in a HTTP Cookie2 header. 40 | */ 41 | HttpCookie(const QByteArray source); 42 | 43 | /** Convert this cookie to a string that may be used in a Set-Cookie2 header. */ 44 | QByteArray toByteArray() const ; 45 | 46 | /** 47 | Split a string list into parts, where each part is delimited by semicolon. 48 | Semicolons within double quotes are skipped. Double quotes are removed. 49 | */ 50 | static QList splitCSV(const QByteArray source); 51 | 52 | /** Set the name of this cookie */ 53 | void setName(const QByteArray name); 54 | 55 | /** Set the value of this cookie */ 56 | void setValue(const QByteArray value); 57 | 58 | /** Set the comment of this cookie */ 59 | void setComment(const QByteArray comment); 60 | 61 | /** Set the domain of this cookie */ 62 | void setDomain(const QByteArray domain); 63 | 64 | /** Set the maximum age of this cookie in seconds. 0=discard immediately */ 65 | void setMaxAge(const int maxAge); 66 | 67 | /** Set the path for that the cookie will be sent, default="/" which means the whole domain */ 68 | void setPath(const QByteArray path); 69 | 70 | /** Set secure mode, so that the cokkie will only be sent on secure connections */ 71 | void setSecure(const bool secure); 72 | 73 | /** Get the name of this cookie */ 74 | QByteArray getName() const; 75 | 76 | /** Get the value of this cookie */ 77 | QByteArray getValue() const; 78 | 79 | /** Get the comment of this cookie */ 80 | QByteArray getComment() const; 81 | 82 | /** Get the domain of this cookie */ 83 | QByteArray getDomain() const; 84 | 85 | /** Set the maximum age of this cookie in seconds. */ 86 | int getMaxAge() const; 87 | 88 | /** Set the path of this cookie */ 89 | QByteArray getPath() const; 90 | 91 | /** Get the secure flag of this cookie */ 92 | bool getSecure() const; 93 | 94 | /** Returns always 1 */ 95 | int getVersion() const; 96 | 97 | private: 98 | 99 | QByteArray name; 100 | QByteArray value; 101 | QByteArray comment; 102 | QByteArray domain; 103 | int maxAge; 104 | QByteArray path; 105 | bool secure; 106 | int version; 107 | 108 | }; 109 | 110 | #endif // HTTPCOOKIE_H 111 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHimage.h: -------------------------------------------------------------------------------- 1 | /** @file eHimage.h 2 | * @brief Basic image type and operations 3 | * @note images are stored using column major style 4 | * 5 | * @author Hang Su 6 | * @date 2012-08-13 7 | */ 8 | #ifndef EHIMAGE_H 9 | #define EHIMAGE_H 10 | 11 | #include 12 | #include 13 | 14 | #include "eHbox.h" 15 | #include "eHbbox.h" 16 | 17 | /** @struct eHimage 18 | * @brief Basic image data structure 19 | * @note Using column major (Fortran) style 20 | * @note Data is associated with (double* data), (double* ch[3]) only 21 | * provide a view into data 22 | */ 23 | struct image_t { 24 | double* data; /**< @brief pixel value data */ 25 | double* ch[3]; /**< @brief a view into each channel */ 26 | size_t sizy; /**< @brief image height */ 27 | size_t sizx; /**< @brief image width */ 28 | size_t nchannel; /**< @brief number of channels */ 29 | int imsize[3]; /**< @brief [sizy sizx nchanel] */ 30 | bool is_shared; /**< @brief whether share data with a parent image */ 31 | size_t stepy; /**< @brief step between columns */ 32 | size_t stepyx; /**< @brief step between channels */ 33 | }; 34 | 35 | /** @brief Allocate a new image of size [sizy, sizx, nch] 36 | * @return pointer to the allocated image 37 | * @note Returned image is not initialized 38 | */ 39 | image_t* image_alloc(size_t sizy, size_t sizx, size_t nch = 3); 40 | 41 | /** @brief Allocate a new image of size [sizy, sizx, nch], and initialize 42 | * all pixel values to fill 43 | * @return pointer to the allocated image 44 | */ 45 | image_t* image_alloc(size_t sizy, size_t sizx, size_t nch, const double* fillval); 46 | 47 | /** @brief Delete image and associated memory 48 | * @note If it's a shared image(the "child"), no data is destroyed; if the image 49 | * that owns the data is deleted, all descendants are not accessible anymore 50 | */ 51 | void image_delete(image_t* img); 52 | 53 | /** @brief Fill all pixels with same values 54 | * @param img target 55 | * @param val value to be filled to each pixel, it should be at least 56 | * the same length as img->nchannel 57 | */ 58 | void image_fill(image_t* img, const double* val); 59 | 60 | /** @brief Read Jpeg image file 61 | * @return pointer to allocatd image, NULL if failed 62 | */ 63 | image_t* image_readJPG(const char* filename); 64 | 65 | /** @brief Fast image subsampling 66 | * 67 | * Unlike image_resize(), this function can only be used to down-scale 68 | * an image, and focus more on anti-aliasing when building pyramid 69 | * @param img the image to be subsampled 70 | * @param scale subsample scale (<1) 71 | * @return subsampleded image, or NULL if scale>1 72 | * @sa image_resize() 73 | * @note input image remains alive and unchanged 74 | */ 75 | image_t* image_subsample(const image_t* img, double scale); 76 | 77 | /** @brief Get an image half the size of input one 78 | * @param img the image to be reduced 79 | * @return reduced image 80 | * @note input image remains alive and unchanged 81 | */ 82 | image_t* image_reduce(const image_t* img); 83 | 84 | /** @brief Resize an image using bilateral interpolation 85 | * @param image the image to be resized 86 | * @param scale resizing scale 87 | * @return resized image 88 | * @sa image_subsample() 89 | * @note input image remains alive and unchanged 90 | */ 91 | image_t* image_resize(const image_t* img, double scale); 92 | 93 | /** @brief Crop image 94 | * This function can be used in two ways, either get shared data from original 95 | * image, or allocate a new image, which is more expensive. 96 | * @param img original image 97 | * @param crop crop area within img 98 | * @param store offset [offy offx] of the cropped patch inside image if not NULL 99 | * @param shared indicate whether the result shares data with original image 100 | * @return cropped image patch, NULL if allocation failed 101 | */ 102 | image_t* image_crop(const image_t* img, fbox_t crop, int* offset=NULL, bool shared=true); 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/test/resources/profile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "face": { 4 | "x1": 220.877797, 5 | "x2": 387.184013, 6 | "y1": 129.480088, 7 | "y2": 295.786304 8 | }, 9 | "pose": 90, 10 | "model": "profile", 11 | "parts": { 12 | "nose bottom to top": [ 13 | { 14 | "x": 353.537994, 15 | "y": 216.441437, 16 | "num": 0 17 | }, 18 | { 19 | "x": 368.770935, 20 | "y": 208.824951, 21 | "num": 1 22 | }, 23 | { 24 | "x": 368.770935, 25 | "y": 201.208481, 26 | "num": 2 27 | }, 28 | { 29 | "x": 361.154480, 30 | "y": 185.975525, 31 | "num": 3 32 | }, 33 | { 34 | "x": 353.537994, 35 | "y": 170.742584, 36 | "num": 4 37 | }, 38 | { 39 | "x": 345.921509, 40 | "y": 163.126099, 41 | "num": 5 42 | } 43 | ], 44 | "eye bottom to top": [ 45 | { 46 | "x": 338.305054, 47 | "y": 170.742584, 48 | "num": 6 49 | }, 50 | { 51 | "x": 330.688568, 52 | "y": 170.742584, 53 | "num": 7 54 | }, 55 | { 56 | "x": 323.072083, 57 | "y": 163.126099, 58 | "num": 8 59 | }, 60 | { 61 | "x": 330.688568, 62 | "y": 163.126099, 63 | "num": 9 64 | }, 65 | { 66 | "x": 338.305054, 67 | "y": 163.126099, 68 | "num": 10 69 | } 70 | ], 71 | "eyebrow nose to ear": [ 72 | { 73 | "x": 338.305054, 74 | "y": 147.893158, 75 | "num": 11 76 | }, 77 | { 78 | "x": 330.688568, 79 | "y": 147.893158, 80 | "num": 12 81 | }, 82 | { 83 | "x": 315.455627, 84 | "y": 147.893158, 85 | "num": 13 86 | }, 87 | { 88 | "x": 307.839142, 89 | "y": 155.509628, 90 | "num": 14 91 | } 92 | ], 93 | "mouth": [ 94 | { 95 | "x": 353.537994, 96 | "y": 231.674393, 97 | "num": 15 98 | }, 99 | { 100 | "x": 345.921509, 101 | "y": 231.674393, 102 | "num": 16 103 | }, 104 | { 105 | "x": 338.305054, 106 | "y": 231.674393, 107 | "num": 17 108 | }, 109 | { 110 | "x": 330.688568, 111 | "y": 231.674393, 112 | "num": 18 113 | }, 114 | { 115 | "x": 330.688568, 116 | "y": 239.290863, 117 | "num": 19 118 | }, 119 | { 120 | "x": 338.305054, 121 | "y": 246.907333, 122 | "num": 20 123 | }, 124 | { 125 | "x": 345.921509, 126 | "y": 246.907333, 127 | "num": 21 128 | }, 129 | { 130 | "x": 338.305054, 131 | "y": 231.674393, 132 | "num": 22 133 | }, 134 | { 135 | "x": 345.921509, 136 | "y": 231.674393, 137 | "num": 23 138 | }, 139 | { 140 | "x": 345.921509, 141 | "y": 231.674393, 142 | "num": 24 143 | }, 144 | { 145 | "x": 345.921509, 146 | "y": 239.290863, 147 | "num": 25 148 | }, 149 | { 150 | "x": 345.921509, 151 | "y": 239.290863, 152 | "num": 26 153 | }, 154 | { 155 | "x": 338.305054, 156 | "y": 254.523819, 157 | "num": 27 158 | } 159 | ], 160 | "jaw bottom to top": [ 161 | { 162 | "x": 338.305054, 163 | "y": 269.756775, 164 | "num": 28 165 | }, 166 | { 167 | "x": 323.072083, 168 | "y": 277.373230, 169 | "num": 29 170 | }, 171 | { 172 | "x": 307.839142, 173 | "y": 277.373230, 174 | "num": 30 175 | }, 176 | { 177 | "x": 292.606201, 178 | "y": 269.756775, 179 | "num": 31 180 | }, 181 | { 182 | "x": 277.373230, 183 | "y": 262.140289, 184 | "num": 32 185 | }, 186 | { 187 | "x": 262.140289, 188 | "y": 254.523819, 189 | "num": 33 190 | }, 191 | { 192 | "x": 254.523819, 193 | "y": 246.907333, 194 | "num": 34 195 | }, 196 | { 197 | "x": 254.523819, 198 | "y": 231.674393, 199 | "num": 35 200 | }, 201 | { 202 | "x": 246.907333, 203 | "y": 216.441437, 204 | "num": 36 205 | }, 206 | { 207 | "x": 239.290863, 208 | "y": 201.208481, 209 | "num": 37 210 | }, 211 | { 212 | "x": 239.290863, 213 | "y": 185.975525, 214 | "num": 38 215 | } 216 | ] 217 | } 218 | } 219 | ] -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpsessionstore.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httpsessionstore.h" 7 | #include 8 | #include 9 | 10 | HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent) 11 | :QObject(parent) 12 | { 13 | this->settings=settings; 14 | connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(timerEvent())); 15 | cleanupTimer.start(60000); 16 | cookieName=settings->value("cookieName","sessionid").toByteArray(); 17 | expirationTime=settings->value("expirationTime",3600000).toInt(); 18 | qDebug("HttpSessionStore: Sessions expire after %i milliseconds",expirationTime); 19 | } 20 | 21 | HttpSessionStore::~HttpSessionStore() 22 | { 23 | cleanupTimer.stop(); 24 | } 25 | 26 | QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& response) { 27 | // The session ID in the response has priority because this one will be used in the next request. 28 | mutex.lock(); 29 | // Get the session ID from the response cookie 30 | QByteArray sessionId=response.getCookies().value(cookieName).getValue(); 31 | if (sessionId.isEmpty()) { 32 | // Get the session ID from the request cookie 33 | sessionId=request.getCookie(cookieName); 34 | } 35 | // Clear the session ID if there is no such session in the storage. 36 | if (!sessionId.isEmpty()) { 37 | if (!sessions.contains(sessionId)) { 38 | qDebug("HttpSessionStore: received invalid session cookie with ID %s",sessionId.data()); 39 | sessionId.clear(); 40 | } 41 | } 42 | mutex.unlock(); 43 | return sessionId; 44 | } 45 | 46 | HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& response, bool allowCreate) { 47 | QByteArray sessionId=getSessionId(request,response); 48 | mutex.lock(); 49 | if (!sessionId.isEmpty()) { 50 | HttpSession session=sessions.value(sessionId); 51 | if (!session.isNull()) { 52 | mutex.unlock(); 53 | session.setLastAccess(); 54 | return session; 55 | } 56 | } 57 | // Need to create a new session 58 | if (allowCreate) { 59 | QByteArray cookieName=settings->value("cookieName","sessionid").toByteArray(); 60 | QByteArray cookiePath=settings->value("cookiePath").toByteArray(); 61 | QByteArray cookieComment=settings->value("cookieComment").toByteArray(); 62 | QByteArray cookieDomain=settings->value("cookieDomain").toByteArray(); 63 | HttpSession session(true); 64 | qDebug("HttpSessionStore: create new session with ID %s",session.getId().data()); 65 | sessions.insert(session.getId(),session); 66 | response.setCookie(HttpCookie(cookieName,session.getId(),expirationTime/1000,cookiePath,cookieComment,cookieDomain)); 67 | mutex.unlock(); 68 | return session; 69 | } 70 | // Return a null session 71 | mutex.unlock(); 72 | return HttpSession(); 73 | } 74 | 75 | HttpSession HttpSessionStore::getSession(const QByteArray id) { 76 | mutex.lock(); 77 | HttpSession session=sessions.value(id); 78 | mutex.unlock(); 79 | session.setLastAccess(); 80 | return session; 81 | } 82 | 83 | void HttpSessionStore::timerEvent() { 84 | // Todo: find a way to delete sessions only if no controller is accessing them 85 | mutex.lock(); 86 | qint64 now=QDateTime::currentMSecsSinceEpoch(); 87 | QMap::iterator i = sessions.begin(); 88 | while (i != sessions.end()) { 89 | QMap::iterator prev = i; 90 | ++i; 91 | HttpSession session=prev.value(); 92 | qint64 lastAccess=session.getLastAccess(); 93 | if (now-lastAccess>expirationTime) { 94 | qDebug("HttpSessionStore: session %s expired",session.getId().data()); 95 | sessions.erase(prev); 96 | } 97 | } 98 | mutex.unlock(); 99 | } 100 | 101 | 102 | /** Delete a session */ 103 | void HttpSessionStore::removeSession(HttpSession session) { 104 | mutex.lock(); 105 | sessions.remove(session.getId()); 106 | mutex.unlock(); 107 | } 108 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpresponse.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httpresponse.h" 7 | 8 | HttpResponse::HttpResponse(QTcpSocket* socket) { 9 | this->socket=socket; 10 | statusCode=200; 11 | statusText="OK"; 12 | sentHeaders=false; 13 | sentLastPart=false; 14 | } 15 | 16 | void HttpResponse::setHeader(QByteArray name, QByteArray value) { 17 | Q_ASSERT(sentHeaders==false); 18 | headers.insert(name,value); 19 | } 20 | 21 | void HttpResponse::setHeader(QByteArray name, int value) { 22 | Q_ASSERT(sentHeaders==false); 23 | headers.insert(name,QByteArray::number(value)); 24 | } 25 | 26 | QMap& HttpResponse::getHeaders() { 27 | return headers; 28 | } 29 | 30 | void HttpResponse::setStatus(int statusCode, QByteArray description) { 31 | this->statusCode=statusCode; 32 | statusText=description; 33 | } 34 | 35 | int HttpResponse::getStatus() { 36 | return statusCode; 37 | } 38 | 39 | QByteArray HttpResponse::getStatusText() const { 40 | return statusText; 41 | } 42 | 43 | void HttpResponse::writeHeaders() { 44 | Q_ASSERT(sentHeaders==false); 45 | QByteArray buffer; 46 | buffer.append("HTTP/1.1 "); 47 | buffer.append(QByteArray::number(statusCode)); 48 | buffer.append(' '); 49 | buffer.append(statusText); 50 | buffer.append("\r\n"); 51 | foreach(QByteArray name, headers.keys()) { 52 | buffer.append(name); 53 | buffer.append(": "); 54 | buffer.append(headers.value(name)); 55 | buffer.append("\r\n"); 56 | } 57 | foreach(HttpCookie cookie,cookies.values()) { 58 | buffer.append("Set-Cookie: "); 59 | buffer.append(cookie.toByteArray()); 60 | buffer.append("\r\n"); 61 | } 62 | buffer.append("\r\n"); 63 | writeToSocket(buffer); 64 | sentHeaders=true; 65 | } 66 | 67 | bool HttpResponse::writeToSocket(QByteArray data) { 68 | int remaining=data.size(); 69 | char* ptr=data.data(); 70 | while (socket->isOpen() && remaining>0) { 71 | // Wait until the previous buffer content is written out, otherwise it could become very large 72 | socket->waitForBytesWritten(-1); 73 | int written=socket->write(ptr,remaining); 74 | if (written==-1) { 75 | return false; 76 | } 77 | ptr+=written; 78 | remaining-=written; 79 | } 80 | return true; 81 | } 82 | 83 | void HttpResponse::write(QByteArray data, bool lastPart) { 84 | Q_ASSERT(sentLastPart==false); 85 | if (sentHeaders==false) { 86 | QByteArray connectionMode=headers.value("Connection"); 87 | if (!headers.contains("Content-Length") && !headers.contains("Transfer-Encoding") && connectionMode!="close" && connectionMode!="Close") { 88 | if (!lastPart) { 89 | headers.insert("Transfer-Encoding","chunked"); 90 | } 91 | else { 92 | headers.insert("Content-Length",QByteArray::number(data.size())); 93 | } 94 | } 95 | writeHeaders(); 96 | } 97 | bool chunked=headers.value("Transfer-Encoding")=="chunked" || headers.value("Transfer-Encoding")=="Chunked"; 98 | if (chunked) { 99 | if (data.size()>0) { 100 | QByteArray buffer=QByteArray::number(data.size(),16); 101 | buffer.append("\r\n"); 102 | writeToSocket(buffer); 103 | writeToSocket(data); 104 | writeToSocket("\r\n"); 105 | } 106 | } 107 | else { 108 | writeToSocket(data); 109 | } 110 | if (lastPart) { 111 | if (chunked) { 112 | writeToSocket("0\r\n\r\n"); 113 | } 114 | else if (!headers.contains("Content-Length")) { 115 | socket->disconnectFromHost(); 116 | } 117 | sentLastPart=true; 118 | } 119 | } 120 | 121 | 122 | bool HttpResponse::hasSentLastPart() const { 123 | return sentLastPart; 124 | } 125 | 126 | 127 | void HttpResponse::setCookie(const HttpCookie& cookie) { 128 | Q_ASSERT(sentHeaders==false); 129 | if (!cookie.getName().isEmpty()) { 130 | cookies.insert(cookie.getName(),cookie); 131 | } 132 | } 133 | 134 | QMap& HttpResponse::getCookies() { 135 | return cookies; 136 | } 137 | 138 | void HttpResponse::redirect(const QByteArray& url) { 139 | setStatus(303,"See Other"); 140 | setHeader("Location",url); 141 | write("Redirect",true); 142 | } 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Status 2 | [![Build Status](https://travis-ci.org/eHarmony/face-parts-service.svg?branch=master)](https://travis-ci.org/eHarmony/face-parts-service) 3 | 4 | # Executive Summary 5 | 6 | This is a RESTful API for segmenting human faces from an image. The software is powered by [face parts](http://www.ics.uci.edu/~xzhu/face/) using [CImg](http://cimg.sourceforge.net/) for image loading. The goal of this project is threefold: 7 | 8 | 1. Remove the dependency on Matlab 9 | 2. Make the code more usable by wrapping it in a RESTful API. This makes use of a modified version of [QtWebApp](http://stefanfrings.de/qtwebapp/index-en.html) 10 | 3. Speed up the code by making use of [Threaded Building Blocks](https://www.threadingbuildingblocks.org/) 11 | 12 | Here is an example of the output 13 | 14 | ![Submitting an image with Postman](/images/regular.jpg) 15 | 16 | # Mac Necessary Libraries (Tested on OSX 10.9.4) 17 | 18 | 1. Install XCode developer tools and homebrew 19 | 2. brew install libjpeg-turbo 20 | 3. brew install tbb 21 | 4. Install MacPorts from [here](https://www.macports.org/install.php) 22 | 5. sudo port install atlas +clang+nofortran (might take a while) 23 | 6. Download and install Qt 5.2.1 from [here](http://qt-project.org/downloads) 24 | 25 | # Red Hat Necessary Libraries (Tested on Centos 6.5) 26 | 27 | 1. sudo yum install atlas-devel.x86_64 28 | 2. sudo yum install libjpeg-devel 29 | 3. sudo yum install tbb-devel 30 | 4. sudo yum install qt5-qtbase-devel 31 | 32 | # Ubuntu Necessary Libraries (Tested on Ubuntu 14.04) 33 | 34 | 1. sudo apt-get install libatlas-base-dev 35 | 2. sudo apt-get install libjpeg-dev 36 | 3. sudo apt-get install libtbb-dev 37 | 4. sudo apt-get install qt5-default 38 | 5. sudo apt-get install maven 39 | 40 | (for running the code, don't install the devel versions, just use the regular versions) 41 | 42 | # Make Instructions 43 | We are using maven as a build architecture. I have tested this using [maven version 3.0.5](http://maven.apache.org/download.cgi). 44 | 45 | 1. Add qmake to your path 46 | 2. mvn install 47 | 48 | # Running the Server 49 | 50 | In order to start the webserver run `target/classes/face-parts-service src/main/resources/configfile.ini`. At this point, the server is ready to segment images. In order to submit an image, create a multipart form and submit a JPEG file (right now the server only works with JPEGs) to http://localhost:8084/face-parts/generate with a key named "file". An example of submitting a file using the [Chrome](https://www.google.com/intl/en-US/chrome/browser/) web browser extension [Postman](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en) can be seen below. 51 | 52 | ![Submitting an image with Postman](/images/postman_demo.png) 53 | 54 | If the request was successful, a 200 response will be returned along with some JSON described here, where the outer array is a list of all the faces found: 55 | 56 | [ 57 | "face": { 58 | "x1": , 59 | "y1": , 60 | "x2": , 61 | "y2": 62 | }, 63 | "pose": , 64 | "model": , 65 | "parts": { 66 | : [ 67 | { 68 | "x": , 69 | "y": , 70 | "num": 71 | } 72 | ] 73 | } 74 | ] 75 | 76 | The server is also able to return an image with the information about the face overlayed on it. In order to do that, submit the same request as above to http://localhost:8084/face-parts/generate.jpg 77 | 78 | If you want to get just a list of the points instead of the descriptive regions, submit the request to http://localhost:8084/face-parts/generate?points=inline 79 | 80 | # Configuration 81 | 82 | The webserver can be configured in a variety of ways. An example configuration file is found at `src/main/resources/configfile.ini`. It is very important that the lines 83 | 84 | face_model=src/main/resources/face_p146.xml 85 | pose_model=src/main/resources/pose_BUFFY.xml 86 | 87 | point to valid files, as these represent the models used for segmentation. These paths are relative to the directory the program was started from. 88 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/filelogger.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef FILELOGGER_H 7 | #define FILELOGGER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "logger.h" 15 | 16 | /** 17 | Logger that uses a text file for output. Settings are read from a 18 | config file using a QSettings object. Config settings can be changed at runtime. 19 |

20 | Example for the configuration settings: 21 |

 22 |   fileName=logs/QtWebApp.log
 23 |   maxSize=1000000
 24 |   maxBackups=2
 25 |   minLevel=0
 26 |   msgformat={timestamp} {typeNr} {type} thread={thread}: {msg}
 27 |   timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
 28 |   bufferSize=0
 29 |   
30 | 31 | - fileName is the name of the log file, relative to the directory of the settings file. 32 | In case of windows, if the settings are in the registry, the path is relative to the current 33 | working directory. 34 | - maxSize is the maximum size of that file in bytes. The file will be backed up and 35 | replaced by a new file if it becomes larger than this limit. Please note that 36 | the actual file size may become a little bit larger than this limit. Default is 0=unlimited. 37 | - maxBackups defines the number of backup files to keep. Default is 0=unlimited. 38 | - minLevel defines the minimum type of messages that are written (together with buffered messages) into the file. Defaults is 0=debug. 39 | - msgFormat defines the decoration of log messages, see LogMessage class. Default is "{timestamp} {type} {msg}". 40 | - timestampFormat defines the format of timestamps, see QDateTime::toString(). Default is "yyyy-MM-dd hh:mm:ss.zzz". 41 | - bufferSize defines the size of the buffer. Default is 0=disabled. 42 | 43 | @see set() describes how to set logger variables 44 | @see LogMessage for a description of the message decoration. 45 | @see Logger for a descrition of the buffer. 46 | */ 47 | 48 | class FileLogger : public Logger { 49 | Q_OBJECT 50 | Q_DISABLE_COPY(FileLogger) 51 | public: 52 | 53 | /** 54 | Constructor. 55 | @param settings Configuration settings, usually stored in an INI file. Must not be 0. 56 | Settings are read from the current group, so the caller must have called settings->beginGroup(). 57 | Because the group must not change during runtime, it is recommended to provide a 58 | separate QSettings instance to the logger that is not used by other parts of the program. 59 | @param refreshInterval Interval of checking for changed config settings in msec, or 0=disabled 60 | @param parent Parent object 61 | */ 62 | FileLogger(QSettings* settings, const int refreshInterval=10000, QObject* parent = 0); 63 | 64 | /** 65 | Destructor. Closes the file. 66 | */ 67 | virtual ~FileLogger(); 68 | 69 | /** Write a message to the log file */ 70 | virtual void write(const LogMessage* logMessage); 71 | 72 | protected: 73 | 74 | /** 75 | Handler for timer events. 76 | Refreshes config settings or synchronizes I/O buffer, depending on the event. 77 | This method is thread-safe. 78 | @param event used to distinguish between the two timers. 79 | */ 80 | void timerEvent(QTimerEvent* event); 81 | 82 | private: 83 | 84 | /** Configured name of the log file */ 85 | QString fileName; 86 | 87 | /** Configured maximum size of the file in bytes, or 0=unlimited */ 88 | long maxSize; 89 | 90 | /** Configured maximum number of backup files, or 0=unlimited */ 91 | int maxBackups; 92 | 93 | /** Pointer to the configuration settings */ 94 | QSettings* settings; 95 | 96 | /** Output file, or 0=disabled */ 97 | QFile* file; 98 | 99 | /** Timer for refreshing configuration settings */ 100 | QBasicTimer refreshTimer; 101 | 102 | /** Timer for flushing the file I/O buffer */ 103 | QBasicTimer flushTimer; 104 | 105 | /** Open the output file */ 106 | void open(); 107 | 108 | /** Close the output file */ 109 | void close(); 110 | 111 | /** Rotate files and delete some backups if there are too many */ 112 | void rotate(); 113 | 114 | /** 115 | Refreshes the configuration settings. 116 | This method is thread-safe. 117 | */ 118 | void refreshSettings(); 119 | 120 | }; 121 | 122 | #endif // FILELOGGER_H 123 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpsession.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httpsession.h" 7 | #include 8 | #include 9 | 10 | 11 | HttpSession::HttpSession(bool canStore) { 12 | if (canStore) { 13 | dataPtr=new HttpSessionData(); 14 | dataPtr->refCount=1; 15 | dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); 16 | dataPtr->id=QUuid::createUuid().toString().toLocal8Bit(); 17 | #ifdef SUPERVERBOSE 18 | qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); 19 | #endif 20 | } 21 | else { 22 | dataPtr=0; 23 | } 24 | } 25 | 26 | HttpSession::HttpSession(const HttpSession& other) { 27 | dataPtr=other.dataPtr; 28 | if (dataPtr) { 29 | dataPtr->lock.lockForWrite(); 30 | dataPtr->refCount++; 31 | #ifdef SUPERVERBOSE 32 | qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); 33 | #endif 34 | dataPtr->lock.unlock(); 35 | } 36 | } 37 | 38 | HttpSession& HttpSession::operator= (const HttpSession& other) { 39 | HttpSessionData* oldPtr=dataPtr; 40 | dataPtr=other.dataPtr; 41 | if (dataPtr) { 42 | dataPtr->lock.lockForWrite(); 43 | dataPtr->refCount++; 44 | #ifdef SUPERVERBOSE 45 | qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); 46 | #endif 47 | dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); 48 | dataPtr->lock.unlock(); 49 | } 50 | if (oldPtr) { 51 | int refCount; 52 | oldPtr->lock.lockForRead(); 53 | refCount=oldPtr->refCount--; 54 | #ifdef SUPERVERBOSE 55 | qDebug("HttpSession: refCount of %s is %i",oldPtr->id.data(),oldPtr->refCount); 56 | #endif 57 | oldPtr->lock.unlock(); 58 | if (refCount==0) { 59 | delete oldPtr; 60 | } 61 | } 62 | return *this; 63 | } 64 | 65 | HttpSession::~HttpSession() { 66 | if (dataPtr) { 67 | int refCount; 68 | dataPtr->lock.lockForRead(); 69 | refCount=--dataPtr->refCount; 70 | #ifdef SUPERVERBOSE 71 | qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); 72 | #endif 73 | dataPtr->lock.unlock(); 74 | if (refCount==0) { 75 | qDebug("HttpSession: deleting data"); 76 | delete dataPtr; 77 | } 78 | } 79 | } 80 | 81 | 82 | QByteArray HttpSession::getId() const { 83 | if (dataPtr) { 84 | return dataPtr->id; 85 | } 86 | else { 87 | return QByteArray(); 88 | } 89 | } 90 | 91 | bool HttpSession::isNull() const { 92 | return dataPtr==0; 93 | } 94 | 95 | void HttpSession::set(const QByteArray& key, const QVariant& value) { 96 | if (dataPtr) { 97 | dataPtr->lock.lockForWrite(); 98 | dataPtr->values.insert(key,value); 99 | dataPtr->lock.unlock(); 100 | } 101 | } 102 | 103 | void HttpSession::remove(const QByteArray& key) { 104 | if (dataPtr) { 105 | dataPtr->lock.lockForWrite(); 106 | dataPtr->values.remove(key); 107 | dataPtr->lock.unlock(); 108 | } 109 | } 110 | 111 | QVariant HttpSession::get(const QByteArray& key) const { 112 | QVariant value; 113 | if (dataPtr) { 114 | dataPtr->lock.lockForRead(); 115 | value=dataPtr->values.value(key); 116 | dataPtr->lock.unlock(); 117 | } 118 | return value; 119 | } 120 | 121 | bool HttpSession::contains(const QByteArray& key) const { 122 | bool found=false; 123 | if (dataPtr) { 124 | dataPtr->lock.lockForRead(); 125 | found=dataPtr->values.contains(key); 126 | dataPtr->lock.unlock(); 127 | } 128 | return found; 129 | } 130 | 131 | QMap HttpSession::getAll() const { 132 | QMap values; 133 | if (dataPtr) { 134 | dataPtr->lock.lockForRead(); 135 | values=dataPtr->values; 136 | dataPtr->lock.unlock(); 137 | } 138 | return values; 139 | } 140 | 141 | qint64 HttpSession::getLastAccess() const { 142 | qint64 value=0; 143 | if (dataPtr) { 144 | dataPtr->lock.lockForRead(); 145 | value=dataPtr->lastAccess; 146 | dataPtr->lock.unlock(); 147 | } 148 | return value; 149 | } 150 | 151 | 152 | void HttpSession::setLastAccess() { 153 | if (dataPtr) { 154 | dataPtr->lock.lockForRead(); 155 | dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); 156 | dataPtr->lock.unlock(); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpresponse.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPRESPONSE_H 7 | #define HTTPRESPONSE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "httpcookie.h" 13 | 14 | /** 15 | This object represents a HTTP response, in particular the response headers. 16 |

17 | Example code for proper response generation: 18 |

 19 |     response.setStatus(200,"OK"); // optional, because this is the default
 20 |     response.writeBody("Hello");
 21 |     response.writeBody("World!",true);
 22 |   
23 |

24 | Example how to return an error: 25 |

 26 |     response.setStatus(500,"server error");
 27 |     response.write("The request cannot be processed because the servers is broken",true);
 28 |   
29 |

30 | For performance reason, writing a single or few large packets is better than writing 31 | many small packets. In case of large responses (e.g. file downloads), a Content-Length 32 | header should be set before calling write(). Web Browsers use that information to display 33 | a progress bar. 34 | */ 35 | 36 | class HttpResponse { 37 | Q_DISABLE_COPY(HttpResponse) 38 | public: 39 | 40 | /** 41 | Constructor. 42 | @param socket used to write the response 43 | */ 44 | HttpResponse(QTcpSocket* socket); 45 | 46 | /** 47 | Set a HTTP response header 48 | @param name name of the header 49 | @param value value of the header 50 | */ 51 | void setHeader(QByteArray name, QByteArray value); 52 | 53 | /** 54 | Set a HTTP response header 55 | @param name name of the header 56 | @param value value of the header 57 | */ 58 | void setHeader(QByteArray name, int value); 59 | 60 | /** Get the map of HTTP response headers */ 61 | QMap& getHeaders(); 62 | 63 | /** Get the map of cookies */ 64 | QMap& getCookies(); 65 | 66 | /** 67 | Set status code and description. The default is 200,OK. 68 | */ 69 | void setStatus(int statusCode, QByteArray description=QByteArray()); 70 | 71 | /** 72 | Get the status code. 73 | */ 74 | int getStatus(); 75 | 76 | /** 77 | Get the status text. 78 | */ 79 | QByteArray getStatusText() const; 80 | 81 | /** 82 | Write body data to the socket. 83 |

84 | The HTTP status line and headers are sent automatically before the first 85 | byte of the body gets sent. 86 |

87 | If the response contains only a single chunk (indicated by lastPart=true), 88 | the response is transferred in traditional mode with a Content-Length 89 | header, which is automatically added if not already set before. 90 |

91 | Otherwise, each part is transferred in chunked mode. 92 | @param data Data bytes of the body 93 | @param lastPart Indicator, if this is the last part of the response. 94 | */ 95 | void write(QByteArray data, bool lastPart=false); 96 | 97 | /** 98 | Indicates wheter the body has been sent completely. Used by the connection 99 | handler to terminate the body automatically when necessary. 100 | */ 101 | bool hasSentLastPart() const; 102 | 103 | /** 104 | Set a cookie. Cookies are sent together with the headers when the first 105 | call to write() occurs. 106 | */ 107 | void setCookie(const HttpCookie& cookie); 108 | 109 | /** 110 | Send a redirect response to the browser. 111 | @param url Destination URL 112 | */ 113 | void redirect(const QByteArray& url); 114 | 115 | private: 116 | 117 | /** Request headers */ 118 | QMap headers; 119 | 120 | /** Socket for writing output */ 121 | QTcpSocket* socket; 122 | 123 | /** HTTP status code*/ 124 | int statusCode; 125 | 126 | /** HTTP status code description */ 127 | QByteArray statusText; 128 | 129 | /** Indicator whether headers have been sent */ 130 | bool sentHeaders; 131 | 132 | /** Indicator whether the body has been sent completely */ 133 | bool sentLastPart; 134 | 135 | /** Cookies */ 136 | QMap cookies; 137 | 138 | /** Write raw data to the socket. This method blocks until all bytes have been passed to the TCP buffer */ 139 | bool writeToSocket(QByteArray data); 140 | 141 | /** 142 | Write the response HTTP status and headers to the socket. 143 | Calling this method is optional, because writeBody() calls 144 | it automatically when required. 145 | */ 146 | void writeHeaders(); 147 | 148 | }; 149 | 150 | #endif // HTTPRESPONSE_H 151 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHfilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHfilter.cpp 3 | * 4 | * 5 | * Hang Su 6 | * 2012-08 @ eH 7 | */ 8 | #include "eHfilter.h" 9 | #include 10 | #include 11 | #include 12 | 13 | using std::vector; 14 | 15 | extern "C" { 16 | #include 17 | } 18 | 19 | struct thread_data { 20 | mat3d_t A; 21 | mat3d_t B; 22 | mat2d_t C; 23 | }; 24 | 25 | /* reshape matrix 26 | * (Old)y x z ==> (F)z x y 27 | * NOTE: result is stored in F, which should be allocated already 28 | */ 29 | double* prepare_filter(double* F, double* Old, size_t sizy, size_t sizx, size_t sizz) { 30 | unsigned f, x, y; 31 | for (f = 0; f < sizz; f++) { 32 | for (x = 0; x < sizx; x++) { 33 | for (y = 0; y < sizy; y++) { 34 | F[f + x*(sizz) + y*(sizx*sizz)] = 35 | Old[y + x*sizy + f*(sizy*sizx)]; 36 | } 37 | } 38 | } 39 | return F; 40 | } 41 | 42 | /* reshape matrix 43 | * (Old)y x z ==> (F)y z x 44 | * NOTE: result is stored in F, which should be allocated already 45 | */ 46 | void prepare_map(double* F, double* Old, size_t sizy, size_t sizx, size_t sizz) { 47 | unsigned f, x, y; 48 | for (f = 0; f < sizz; f++) { 49 | for (x = 0; x < sizx; x++) { 50 | for (y = 0; y < sizy; y++) { 51 | F[y + f*sizy + x*(sizy*sizz)] = 52 | Old[y + x*sizy + f*(sizy*sizx)]; 53 | } 54 | } 55 | } 56 | } 57 | 58 | /* convolve A and B using cblas */ 59 | class convolve { 60 | thread_data* const thread_data_array; 61 | 62 | public: 63 | convolve(thread_data* thread_data) : thread_data_array(thread_data) {} 64 | 65 | void operator()(const tbb::blocked_range& range) const { 66 | for (size_t i=range.begin(); i!=range.end(); ++i) { 67 | run(i); 68 | } 69 | } 70 | 71 | void run(size_t i) const { 72 | thread_data args = thread_data_array[i]; 73 | double *A = args.A.vals; 74 | double *B = args.B.vals; 75 | double *C = args.C.vals; 76 | 77 | for (unsigned x = 0; x < args.C.sizx; x++) { 78 | for (unsigned y = 0; y < args.B.sizz; y++) { 79 | double *A_off = A + x*(args.A.sizy*args.A.sizx) + y; 80 | double *B_off = B + y*(args.B.sizy*args.B.sizx); 81 | double *C_off = C + x*(args.C.sizy); 82 | double one = 1; 83 | int m = args.C.sizy; 84 | int n = args.B.sizy*args.B.sizx; 85 | int lda = args.A.sizy; 86 | int incx = 1; 87 | int incy = 1; 88 | cblas_dgemv(CblasColMajor,CblasNoTrans, m, n, one, A_off, lda, B_off, incx, one, C_off, incy); 89 | } 90 | } 91 | } 92 | }; 93 | 94 | /* 95 | * entry point 96 | * resp = eHconv(cell of B, A, start, end); 97 | */ 98 | mat3d_t* filterv_apply(const vector filters, const mat3d_t* feats, int start, int end, bool multiThreaded) { 99 | //void eHconv(vector& resps, const mat3d_t* feats, const vector filters, int start, int end) { 100 | 101 | size_t len = end-start+1; 102 | assert(end>=start); 103 | assert(len<=filters.size()); 104 | int filter_h = filters[0].w.sizy; 105 | int filter_w = filters[0].w.sizx; 106 | int filter_z = filters[0].w.sizz; 107 | int filter_len = filter_h*filter_w*filter_z; 108 | int height = feats->sizy - filter_h + 1; 109 | int width = feats->sizx - filter_w + 1; 110 | assert(height>=1 && width>=1); 111 | mat3d_t* resps= mat3d_alloc(height, width, filters.size()); 112 | /*XXX necessary */ 113 | for(unsigned i=0;isizx*resps->sizy*resps->sizz;i++) 114 | resps->vals[i]=0; 115 | 116 | double *tmp_filter, *tmp_feats; 117 | thread_data* td = new thread_data[len]; 118 | tmp_feats = new double[feats->sizy*feats->sizx*feats->sizz]; 119 | tmp_filter = new double[filter_len*len]; 120 | 121 | prepare_map(tmp_feats,feats->vals,feats->sizy,feats->sizx,feats->sizz); 122 | 123 | for (size_t i = 0; i < len; ++i) { 124 | td[i].A.vals = tmp_feats; 125 | td[i].A.sizy = feats->sizy; 126 | td[i].A.sizx = feats->sizz; 127 | td[i].A.sizz = feats->sizx; 128 | td[i].B.vals = prepare_filter(tmp_filter+i*filter_len, filters[i].w.vals, 129 | filter_h, filter_w, filter_z); 130 | td[i].B.sizy = filter_z; 131 | td[i].B.sizx = filter_w; 132 | td[i].B.sizz = filter_h; 133 | td[i].C.vals = resps->vals+i*height*width; 134 | td[i].C.sizy = height; 135 | td[i].C.sizx = width; 136 | } 137 | if (multiThreaded) { 138 | tbb::parallel_for(tbb::blocked_range(0, len), convolve(td)); 139 | } 140 | else { 141 | convolve process(td); 142 | for (size_t i=0; i 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void GetFaceResourceTest::initTestCase() { 9 | QString modelResources = property("resources").toString(); 10 | testResources = property("testResources").toString(); 11 | 12 | facemodel_t* faceModel = facemodel_readFromFile(QString(modelResources + "/face_p146.xml").toStdString().c_str()); 13 | posemodel_t* poseModel = posemodel_readFromFile(QString(modelResources + "/pose_BUFFY.xml").toStdString().c_str()); 14 | QSettings settings; 15 | faceResource = new GetFaceResource(faceModel, poseModel, settings); 16 | } 17 | 18 | void GetFaceResourceTest::cleanupTestCase() { 19 | delete faceResource; 20 | } 21 | 22 | void GetFaceResourceTest::noFile() 23 | { 24 | QSettings settings; 25 | HttpRequest request(&settings); 26 | HttpResponse response(NULL); 27 | 28 | faceResource->service(request, response); 29 | 30 | QCOMPARE(HttpHeaders::STATUS_PRECONDITION_FAILED, response.getStatus()); 31 | } 32 | 33 | void GetFaceResourceTest::badFile() { 34 | QFile file(testResources + "/1.json"); 35 | QJsonDocument doc; 36 | int status = faceResource->getJSONFaces(&file, doc); 37 | file.close(); 38 | 39 | QCOMPARE(HttpHeaders::STATUS_UNSUPPORTED_MEDIA_TYPE, status); 40 | QCOMPARE(QJsonDocument(), doc); 41 | } 42 | 43 | void GetFaceResourceTest::goodFace() { 44 | testFaces("1"); 45 | } 46 | 47 | void GetFaceResourceTest::profile() { 48 | testFaces("profile"); 49 | } 50 | 51 | void GetFaceResourceTest::faceFromPose() { 52 | testFaces("10156670_4"); 53 | } 54 | 55 | void GetFaceResourceTest::grayscale() { 56 | testFaces("grayscale"); 57 | } 58 | 59 | void GetFaceResourceTest::cmyk() { 60 | testFaces("cmyk"); 61 | } 62 | 63 | void GetFaceResourceTest::cmykAlternate() { 64 | testFaces("cmykAlternate"); 65 | } 66 | 67 | void GetFaceResourceTest::noFace() { 68 | testNoFaces("48170621_4"); 69 | } 70 | 71 | void GetFaceResourceTest::testNoFaces(const QString &fileName) { 72 | QFile file(testResources + QDir::separator() + fileName + ".jpg"); 73 | QJsonDocument doc; 74 | int status = faceResource->getJSONFaces(&file, doc); 75 | file.close(); 76 | QJsonArray array; 77 | QJsonDocument emptyDoc(array); 78 | 79 | QCOMPARE(HttpHeaders::STATUS_SUCCESS, status); 80 | QCOMPARE(emptyDoc, doc); 81 | } 82 | 83 | void GetFaceResourceTest::testFaces(const QString& fileName) { 84 | QString base = testResources + QDir::separator() + fileName; 85 | QFile file(base + ".jpg"); 86 | QJsonDocument current; 87 | int status = faceResource->getJSONFaces(&file, current); 88 | file.close(); 89 | QCOMPARE(HttpHeaders::STATUS_SUCCESS, status); 90 | 91 | QFile jsonFile(base + ".json"); 92 | 93 | QVERIFY(jsonFile.exists()); 94 | 95 | jsonFile.open(QIODevice::ReadOnly | QIODevice::Text); 96 | QJsonDocument expected = QJsonDocument().fromJson(jsonFile.readAll()); 97 | jsonFile.close(); 98 | 99 | QJsonArray currentArray = current.array(); 100 | QJsonArray expectedArray = expected.array(); 101 | 102 | // Make sure we're not comparing zero to zero 103 | QVERIFY(expectedArray.size() > 0); 104 | QCOMPARE(currentArray.size(), expectedArray.size()); 105 | 106 | for (int i=0; i expected); 144 | } 145 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/filelogger.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "filelogger.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void FileLogger::refreshSettings() { 18 | mutex.lock(); 19 | // Save old file name for later comparision with new settings 20 | QString oldFileName=fileName; 21 | 22 | // Load new config settings 23 | settings->sync(); 24 | fileName=settings->value("fileName").toString(); 25 | // Convert relative fileName to absolute, based on the directory of the config file. 26 | #ifdef Q_OS_WIN32 27 | if (QDir::isRelativePath(fileName) && settings->format()!=QSettings::NativeFormat) 28 | #else 29 | if (QDir::isRelativePath(fileName)) 30 | #endif 31 | { 32 | QFileInfo configFile(settings->fileName()); 33 | fileName=QFileInfo(configFile.absolutePath(),fileName).absoluteFilePath(); 34 | } 35 | maxSize=settings->value("maxSize",0).toLongLong(); 36 | maxBackups=settings->value("maxBackups",0).toInt(); 37 | msgFormat=settings->value("msgFormat","{timestamp} {type} {msg}").toString(); 38 | timestampFormat=settings->value("timestampFormat","yyyy-MM-dd hh:mm:ss.zzz").toString(); 39 | minLevel=static_cast(settings->value("minLevel",0).toInt()); 40 | bufferSize=settings->value("bufferSize",0).toInt(); 41 | 42 | // Create new file if the filename has been changed 43 | if (oldFileName!=fileName) { 44 | fprintf(stderr,"Logging to %s\n",qPrintable(fileName)); 45 | close(); 46 | open(); 47 | } 48 | mutex.unlock(); 49 | } 50 | 51 | 52 | FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* parent) 53 | : Logger(parent) 54 | { 55 | Q_ASSERT(settings!=0); 56 | Q_ASSERT(refreshInterval>=0); 57 | this->settings=settings; 58 | file=0; 59 | if (refreshInterval>0) { 60 | refreshTimer.start(refreshInterval,this); 61 | } 62 | flushTimer.start(1000,this); 63 | refreshSettings(); 64 | } 65 | 66 | 67 | FileLogger::~FileLogger() { 68 | close(); 69 | } 70 | 71 | 72 | void FileLogger::write(const LogMessage* logMessage) { 73 | // Try to write to the file 74 | if (file) { 75 | 76 | // Write the message 77 | file->write(qPrintable(logMessage->toString(msgFormat,timestampFormat))); 78 | 79 | // Flush error messages immediately, to ensure that no important message 80 | // gets lost when the program terinates abnormally. 81 | if (logMessage->getType()>=QtCriticalMsg) { 82 | file->flush(); 83 | } 84 | 85 | // Check for success 86 | if (file->error()) { 87 | close(); 88 | qWarning("Cannot write to log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); 89 | } 90 | 91 | } 92 | 93 | // Fall-back to the super class method, if writing failed 94 | if (!file) { 95 | Logger::write(logMessage); 96 | } 97 | 98 | } 99 | 100 | void FileLogger::open() { 101 | if (fileName.isEmpty()) { 102 | qWarning("Name of logFile is empty"); 103 | } 104 | else { 105 | file=new QFile(fileName); 106 | if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { 107 | qWarning("Cannot open log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); 108 | file=0; 109 | } 110 | } 111 | } 112 | 113 | 114 | void FileLogger::close() { 115 | if (file) { 116 | file->close(); 117 | delete file; 118 | file=0; 119 | } 120 | } 121 | 122 | void FileLogger::rotate() { 123 | // count current number of existing backup files 124 | int count=0; 125 | forever { 126 | QFile bakFile(QString("%1.%2").arg(fileName).arg(count+1)); 127 | if (bakFile.exists()) { 128 | ++count; 129 | } 130 | else { 131 | break; 132 | } 133 | } 134 | 135 | // Remove all old backup files that exceed the maximum number 136 | while (maxBackups>0 && count>=maxBackups) { 137 | QFile::remove(QString("%1.%2").arg(fileName).arg(count)); 138 | --count; 139 | } 140 | 141 | // Rotate backup files 142 | for (int i=count; i>0; --i) { 143 | QFile::rename(QString("%1.%2").arg(fileName).arg(i),QString("%1.%2").arg(fileName).arg(i+1)); 144 | } 145 | 146 | // Backup the current logfile 147 | QFile::rename(fileName,fileName+".1"); 148 | } 149 | 150 | 151 | void FileLogger::timerEvent(QTimerEvent* event) { 152 | if (!event) { 153 | return; 154 | } 155 | else if (event->timerId()==refreshTimer.timerId()) { 156 | refreshSettings(); 157 | } 158 | else if (event->timerId()==flushTimer.timerId() && file) { 159 | mutex.lock(); 160 | 161 | // Flush the I/O buffer 162 | file->flush(); 163 | 164 | // Rotate the file if it is too large 165 | if (maxSize>0 && file->size()>=maxSize) { 166 | close(); 167 | rotate(); 168 | open(); 169 | } 170 | 171 | mutex.unlock(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpcookie.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httpcookie.h" 7 | 8 | HttpCookie::HttpCookie() { 9 | version=1; 10 | maxAge=0; 11 | secure=false; 12 | } 13 | 14 | HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure) { 15 | this->name=name; 16 | this->value=value; 17 | this->maxAge=maxAge; 18 | this->path=path; 19 | this->comment=comment; 20 | this->domain=domain; 21 | this->secure=secure; 22 | this->version=1; 23 | } 24 | 25 | HttpCookie::HttpCookie(const QByteArray source) { 26 | version=1; 27 | maxAge=0; 28 | secure=false; 29 | QList list=splitCSV(source); 30 | foreach(QByteArray part, list) { 31 | 32 | // Split the part into name and value 33 | QByteArray name; 34 | QByteArray value; 35 | int posi=part.indexOf('='); 36 | if (posi) { 37 | name=part.left(posi).trimmed(); 38 | value=part.mid(posi+1).trimmed(); 39 | } 40 | else { 41 | name=part.trimmed(); 42 | value=""; 43 | } 44 | 45 | // Set fields 46 | if (name=="Comment") { 47 | comment=value; 48 | } 49 | else if (name=="Domain") { 50 | domain=value; 51 | } 52 | else if (name=="Max-Age") { 53 | maxAge=value.toInt(); 54 | } 55 | else if (name=="Path") { 56 | path=value; 57 | } 58 | else if (name=="Secure") { 59 | secure=true; 60 | } 61 | else if (name=="Version") { 62 | version=value.toInt(); 63 | } 64 | else { 65 | if (this->name.isEmpty()) { 66 | this->name=name; 67 | this->value=value; 68 | } 69 | else { 70 | qWarning("HttpCookie: Ignoring unknown %s=%s",name.data(),value.data()); 71 | } 72 | } 73 | } 74 | } 75 | 76 | QByteArray HttpCookie::toByteArray() const { 77 | QByteArray buffer(name); 78 | buffer.append('='); 79 | buffer.append(value); 80 | if (!comment.isEmpty()) { 81 | buffer.append("; Comment="); 82 | buffer.append(comment); 83 | } 84 | if (!domain.isEmpty()) { 85 | buffer.append("; Domain="); 86 | buffer.append(domain); 87 | } 88 | if (maxAge!=0) { 89 | buffer.append("; Max-Age="); 90 | buffer.append(QByteArray::number(maxAge)); 91 | } 92 | if (!path.isEmpty()) { 93 | buffer.append("; Path="); 94 | buffer.append(path); 95 | } 96 | if (secure) { 97 | buffer.append("; Secure"); 98 | } 99 | buffer.append("; Version="); 100 | buffer.append(QByteArray::number(version)); 101 | return buffer; 102 | } 103 | 104 | void HttpCookie::setName(const QByteArray name){ 105 | this->name=name; 106 | } 107 | 108 | void HttpCookie::setValue(const QByteArray value){ 109 | this->value=value; 110 | } 111 | 112 | void HttpCookie::setComment(const QByteArray comment){ 113 | this->comment=comment; 114 | } 115 | 116 | void HttpCookie::setDomain(const QByteArray domain){ 117 | this->domain=domain; 118 | } 119 | 120 | void HttpCookie::setMaxAge(const int maxAge){ 121 | this->maxAge=maxAge; 122 | } 123 | 124 | void HttpCookie::setPath(const QByteArray path){ 125 | this->path=path; 126 | } 127 | 128 | void HttpCookie::setSecure(const bool secure){ 129 | this->secure=secure; 130 | } 131 | 132 | QByteArray HttpCookie::getName() const { 133 | return name; 134 | } 135 | 136 | QByteArray HttpCookie::getValue() const { 137 | return value; 138 | } 139 | 140 | QByteArray HttpCookie::getComment() const { 141 | return comment; 142 | } 143 | 144 | QByteArray HttpCookie::getDomain() const { 145 | return domain; 146 | } 147 | 148 | int HttpCookie::getMaxAge() const { 149 | return maxAge; 150 | } 151 | 152 | QByteArray HttpCookie::getPath() const { 153 | return path; 154 | } 155 | 156 | bool HttpCookie::getSecure() const { 157 | return secure; 158 | } 159 | 160 | int HttpCookie::getVersion() const { 161 | return version; 162 | } 163 | 164 | QList HttpCookie::splitCSV(const QByteArray source) { 165 | bool inString=false; 166 | QList list; 167 | QByteArray buffer; 168 | for (int i=0; i 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | Logger* Logger::defaultLogger=0; 14 | 15 | 16 | QThreadStorage*> Logger::logVars; 17 | 18 | 19 | QThreadStorage*> Logger::buffers; 20 | 21 | 22 | QMutex Logger::mutex; 23 | 24 | 25 | Logger::Logger(QObject* parent) 26 | : QObject(parent), 27 | msgFormat("{timestamp} {type} {msg}"), 28 | timestampFormat("dd.MM.yyyy hh:mm:ss.zzz"), 29 | minLevel(QtDebugMsg), 30 | bufferSize(0) 31 | {} 32 | 33 | 34 | Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtMsgType minLevel, const int bufferSize, QObject* parent) 35 | :QObject(parent) { 36 | this->msgFormat=msgFormat; 37 | this->timestampFormat=timestampFormat; 38 | this->minLevel=minLevel; 39 | this->bufferSize=bufferSize; 40 | } 41 | 42 | 43 | void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) { 44 | static QMutex recursiveMutex(QMutex::Recursive); 45 | static QMutex nonRecursiveMutex(QMutex::NonRecursive); 46 | 47 | // Prevent multiple threads from calling this method simultaneoulsy. 48 | // But allow recursive calls, which is required to prevent a deadlock 49 | // if the logger itself produces an error message. 50 | recursiveMutex.lock(); 51 | 52 | // Fall back to stderr when this method has been called recursively. 53 | if (defaultLogger && nonRecursiveMutex.tryLock()) { 54 | defaultLogger->log(type, message, file, function, line); 55 | nonRecursiveMutex.unlock(); 56 | } 57 | else { 58 | fputs(qPrintable(message),stderr); 59 | fflush(stderr); 60 | } 61 | 62 | // Abort the program after logging a fatal message 63 | if (type>=QtFatalMsg) { 64 | abort(); 65 | } 66 | 67 | recursiveMutex.unlock(); 68 | } 69 | 70 | 71 | #if QT_VERSION >= 0x050000 72 | void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) { 73 | (void)(context); // suppress "unused parameter" warning 74 | msgHandler(type,message,context.file,context.function,context.line); 75 | } 76 | #else 77 | void Logger::msgHandler4(const QtMsgType type, const char* message) { 78 | msgHandler(type,message); 79 | } 80 | #endif 81 | 82 | 83 | Logger::~Logger() { 84 | if (defaultLogger==this) { 85 | #if QT_VERSION >= 0x050000 86 | qInstallMessageHandler(0); 87 | #else 88 | qInstallMsgHandler(0); 89 | #endif 90 | defaultLogger=0; 91 | } 92 | } 93 | 94 | 95 | void Logger::write(const LogMessage* logMessage) { 96 | fputs(qPrintable(logMessage->toString(msgFormat,timestampFormat)),stderr); 97 | fflush(stderr); 98 | } 99 | 100 | 101 | void Logger::installMsgHandler() { 102 | defaultLogger=this; 103 | #if QT_VERSION >= 0x050000 104 | qInstallMessageHandler(msgHandler5); 105 | #else 106 | qInstallMsgHandler(msgHandler4); 107 | #endif 108 | } 109 | 110 | 111 | void Logger::set(const QString& name, const QString& value) { 112 | mutex.lock(); 113 | if (!logVars.hasLocalData()) { 114 | logVars.setLocalData(new QHash); 115 | } 116 | logVars.localData()->insert(name,value); 117 | mutex.unlock(); 118 | } 119 | 120 | 121 | void Logger::clear(const bool buffer, const bool variables) { 122 | mutex.lock(); 123 | if (buffer && buffers.hasLocalData()) { 124 | QList* buffer=buffers.localData(); 125 | while (buffer && !buffer->isEmpty()) { 126 | LogMessage* logMessage=buffer->takeLast(); 127 | delete logMessage; 128 | } 129 | } 130 | if (variables && logVars.hasLocalData()) { 131 | logVars.localData()->clear(); 132 | } 133 | mutex.unlock(); 134 | } 135 | 136 | 137 | void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) { 138 | mutex.lock(); 139 | 140 | // If the buffer is enabled, write the message into it 141 | if (bufferSize>0) { 142 | // Create new thread local buffer, if necessary 143 | if (!buffers.hasLocalData()) { 144 | buffers.setLocalData(new QList()); 145 | } 146 | QList* buffer=buffers.localData(); 147 | // Append the decorated log message 148 | LogMessage* logMessage=new LogMessage(type,message,logVars.localData(),file,function,line); 149 | buffer->append(logMessage); 150 | // Delete oldest message if the buffer became too large 151 | if (buffer->size()>bufferSize) { 152 | delete buffer->takeFirst(); 153 | } 154 | // If the type of the message is high enough, print the whole buffer 155 | if (type>=minLevel) { 156 | while (!buffer->isEmpty()) { 157 | LogMessage* logMessage=buffer->takeFirst(); 158 | write(logMessage); 159 | delete logMessage; 160 | } 161 | } 162 | } 163 | 164 | // Buffer is disabled, print the message if the type is high enough 165 | else { 166 | if (type>=minLevel) { 167 | LogMessage logMessage(type,message,logVars.localData(),file,function,line); 168 | write(&logMessage); 169 | } 170 | } 171 | mutex.unlock(); 172 | } 173 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httpconnectionhandler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "httpconnectionhandler.h" 7 | #include "httpresponse.h" 8 | #include 9 | #include 10 | 11 | 12 | HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler) 13 | : QThread() 14 | { 15 | Q_ASSERT(settings!=0); 16 | Q_ASSERT(requestHandler!=0); 17 | this->settings=settings; 18 | this->requestHandler=requestHandler; 19 | currentRequest=0; 20 | busy = false; 21 | // execute signals in my own thread 22 | moveToThread(this); 23 | socket.moveToThread(this); 24 | readTimer.moveToThread(this); 25 | connect(&socket, SIGNAL(readyRead()), SLOT(read())); 26 | connect(&socket, SIGNAL(disconnected()), SLOT(disconnected())); 27 | connect(&readTimer, SIGNAL(timeout()), SLOT(readTimeout())); 28 | readTimer.setSingleShot(true); 29 | qDebug("HttpConnectionHandler (%p): constructed", this); 30 | this->start(); 31 | } 32 | 33 | 34 | HttpConnectionHandler::~HttpConnectionHandler() { 35 | socket.close(); 36 | quit(); 37 | wait(); 38 | qDebug("HttpConnectionHandler (%p): destroyed", this); 39 | } 40 | 41 | 42 | void HttpConnectionHandler::run() { 43 | qDebug("HttpConnectionHandler (%p): thread started", this); 44 | try { 45 | exec(); 46 | } 47 | catch (...) { 48 | qCritical("HttpConnectionHandler (%p): an uncatched exception occured in the thread",this); 49 | } 50 | qDebug("HttpConnectionHandler (%p): thread stopped", this); 51 | } 52 | 53 | 54 | void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) { 55 | qDebug("HttpConnectionHandler (%p): handle new connection", this); 56 | busy = true; 57 | Q_ASSERT(socket.isOpen()==false); // if not, then the handler is already busy 58 | 59 | //UGLY workaround - we need to clear writebuffer before reusing this socket 60 | //https://bugreports.qt-project.org/browse/QTBUG-28914 61 | socket.connectToHost("",0); 62 | socket.abort(); 63 | 64 | if (!socket.setSocketDescriptor(socketDescriptor)) { 65 | qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", this,qPrintable(socket.errorString())); 66 | return; 67 | } 68 | // Start timer for read timeout 69 | int readTimeout=settings->value("readTimeout",10000).toInt(); 70 | readTimer.start(readTimeout); 71 | // delete previous request 72 | delete currentRequest; 73 | currentRequest=NULL; 74 | } 75 | 76 | 77 | bool HttpConnectionHandler::isBusy() { 78 | return busy; 79 | } 80 | 81 | void HttpConnectionHandler::setBusy() { 82 | this->busy = true; 83 | } 84 | 85 | 86 | void HttpConnectionHandler::readTimeout() { 87 | qDebug("HttpConnectionHandler (%p): read timeout occured",this); 88 | 89 | //Commented out because QWebView cannot handle this. 90 | //socket.write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n"); 91 | 92 | socket.disconnectFromHost(); 93 | delete currentRequest; 94 | currentRequest=NULL; 95 | } 96 | 97 | 98 | void HttpConnectionHandler::disconnected() { 99 | qDebug("HttpConnectionHandler (%p): disconnected", this); 100 | socket.close(); 101 | readTimer.stop(); 102 | busy = false; 103 | } 104 | 105 | void HttpConnectionHandler::read() { 106 | #ifdef SUPERVERBOSE 107 | qDebug("HttpConnectionHandler (%p): read input",this); 108 | #endif 109 | 110 | // Create new HttpRequest object if necessary 111 | if (!currentRequest) { 112 | currentRequest=new HttpRequest(settings); 113 | } 114 | 115 | // Collect data for the request object 116 | while (socket.bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) { 117 | currentRequest->readFromSocket(socket); 118 | if (currentRequest->getStatus()==HttpRequest::waitForBody) { 119 | // Restart timer for read timeout, otherwise it would 120 | // expire during large file uploads. 121 | int readTimeout=settings->value("readTimeout",10000).toInt(); 122 | readTimer.start(readTimeout); 123 | } 124 | } 125 | 126 | // If the request is aborted, return error message and close the connection 127 | if (currentRequest->getStatus()==HttpRequest::abort) { 128 | socket.write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n"); 129 | socket.disconnectFromHost(); 130 | delete currentRequest; 131 | currentRequest=NULL; 132 | return; 133 | } 134 | 135 | // If the request is complete, let the request mapper dispatch it 136 | if (currentRequest->getStatus()==HttpRequest::complete) { 137 | readTimer.stop(); 138 | qDebug("HttpConnectionHandler (%p): received request",this); 139 | HttpResponse response(&socket); 140 | try { 141 | requestHandler->service(*currentRequest, response); 142 | } 143 | catch (...) { 144 | qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this); 145 | } 146 | 147 | // Finalize sending the response if not already done 148 | if (!response.hasSentLastPart()) { 149 | response.write(QByteArray(),true); 150 | } 151 | // Close the connection after delivering the response, if requested 152 | if (QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0) { 153 | socket.disconnectFromHost(); 154 | } 155 | else { 156 | // Start timer for next request 157 | int readTimeout=settings->value("readTimeout",10000).toInt(); 158 | readTimer.start(readTimeout); 159 | } 160 | // Prepare for next request 161 | delete currentRequest; 162 | currentRequest=NULL; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/staticfilecontroller.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #include "staticfilecontroller.h" 7 | #include 8 | #include 9 | #include 10 | 11 | StaticFileController::StaticFileController(QSettings* settings, QObject* parent) 12 | :HttpRequestHandler(parent) 13 | { 14 | maxAge=settings->value("maxAge","60000").toInt(); 15 | encoding=settings->value("encoding","UTF-8").toString(); 16 | docroot=settings->value("path",".").toString(); 17 | // Convert relative path to absolute, based on the directory of the config file. 18 | #ifdef Q_OS_WIN32 19 | if (QDir::isRelativePath(docroot) && settings->format()!=QSettings::NativeFormat) 20 | #else 21 | if (QDir::isRelativePath(docroot)) 22 | #endif 23 | { 24 | QFileInfo configFile(settings->fileName()); 25 | docroot=QFileInfo(configFile.absolutePath(),docroot).absoluteFilePath(); 26 | } 27 | qDebug("StaticFileController: docroot=%s, encoding=%s, maxAge=%i",qPrintable(docroot),qPrintable(encoding),maxAge); 28 | maxCachedFileSize=settings->value("maxCachedFileSize","65536").toInt(); 29 | cache.setMaxCost(settings->value("cacheSize","1000000").toInt()); 30 | cacheTimeout=settings->value("cacheTime","60000").toInt(); 31 | qDebug("StaticFileController: cache timeout=%i, size=%i",cacheTimeout,cache.maxCost()); 32 | } 33 | 34 | 35 | void StaticFileController::service(HttpRequest& request, HttpResponse& response) { 36 | QByteArray path=request.getPath(); 37 | // Check if we have the file in cache 38 | qint64 now=QDateTime::currentMSecsSinceEpoch(); 39 | mutex.lock(); 40 | CacheEntry* entry=cache.object(path); 41 | if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) { 42 | QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock. 43 | mutex.unlock(); 44 | qDebug("StaticFileController: Cache hit for %s",path.data()); 45 | setContentType(path,response); 46 | response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); 47 | response.write(document); 48 | } 49 | else { 50 | mutex.unlock(); 51 | // The file is not in cache. 52 | qDebug("StaticFileController: Cache miss for %s",path.data()); 53 | // Forbid access to files outside the docroot directory 54 | if (path.contains("/..")) { 55 | qWarning("StaticFileController: detected forbidden characters in path %s",path.data()); 56 | response.setStatus(403,"forbidden"); 57 | response.write("403 forbidden",true); 58 | return; 59 | } 60 | // If the filename is a directory, append index.html. 61 | if (QFileInfo(docroot+path).isDir()) { 62 | path+="/index.html"; 63 | } 64 | // Try to open the file 65 | QFile file(docroot+path); 66 | qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); 67 | if (file.open(QIODevice::ReadOnly)) { 68 | setContentType(path,response); 69 | response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); 70 | if (file.size()<=maxCachedFileSize) { 71 | // Return the file content and store it also in the cache 72 | entry=new CacheEntry(); 73 | while (!file.atEnd() && !file.error()) { 74 | QByteArray buffer=file.read(65536); 75 | response.write(buffer); 76 | entry->document.append(buffer); 77 | } 78 | entry->created=now; 79 | mutex.lock(); 80 | cache.insert(request.getPath(),entry,entry->document.size()); 81 | mutex.unlock(); 82 | } 83 | else { 84 | // Return the file content, do not store in cache 85 | while (!file.atEnd() && !file.error()) { 86 | response.write(file.read(65536)); 87 | } 88 | } 89 | file.close(); 90 | } 91 | else { 92 | if (file.exists()) { 93 | qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName())); 94 | response.setStatus(403,"forbidden"); 95 | response.write("403 forbidden",true); 96 | } 97 | else { 98 | response.setStatus(404,"not found"); 99 | response.write("404 not found",true); 100 | } 101 | } 102 | } 103 | } 104 | 105 | void StaticFileController::setContentType(QString fileName, HttpResponse& response) const { 106 | if (fileName.endsWith(".png")) { 107 | response.setHeader("Content-Type", "image/png"); 108 | } 109 | else if (fileName.endsWith(".jpg")) { 110 | response.setHeader("Content-Type", "image/jpeg"); 111 | } 112 | else if (fileName.endsWith(".gif")) { 113 | response.setHeader("Content-Type", "image/gif"); 114 | } 115 | else if (fileName.endsWith(".pdf")) { 116 | response.setHeader("Content-Type", "application/pdf"); 117 | } 118 | else if (fileName.endsWith(".txt")) { 119 | response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding)); 120 | } 121 | else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) { 122 | response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding)); 123 | } 124 | else if (fileName.endsWith(".css")) { 125 | response.setHeader("Content-Type", "text/css"); 126 | } 127 | else if (fileName.endsWith(".js")) { 128 | response.setHeader("Content-Type", "text/javascript"); 129 | } 130 | // Todo: add all of your content types 131 | } 132 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHshiftdt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHshiftdt.cpp 3 | * 4 | * Generalized Distance Transform 5 | * see: "Distance Transforms of Sampled Functions" 6 | * (P. F. Felzenszwalb and D. P. Huttenlocher, 2004) 7 | * This applies computes a min convolution of a quadratic function ax^2+bx 8 | * 9 | * Hang Su 10 | * 2012-07 @ eH 11 | */ 12 | #define EH_INF 1E20 13 | #define EH_MAX_LEN 800 14 | 15 | #include 16 | #include 17 | #include "eHmatrix.h" 18 | #include "eHshiftdt.h" 19 | 20 | static inline int square(int x) {return x*x;} 21 | 22 | /* wrapper for default setting */ 23 | void eHshiftdt(double* M, int* Ix, int* Iy, 24 | const double* vals, int sizx, int sizy, 25 | const double* w, bool multiThreaded) { 26 | eHshiftdt(M, Ix, Iy, sizx, sizy, 0, 0, 1, vals, sizx, sizy, w, multiThreaded); 27 | } 28 | 29 | struct shiftData { 30 | const double* src; 31 | double* dst; 32 | int* ptr; 33 | int sstep; 34 | int slen; 35 | double a; 36 | double b; 37 | int dshift; 38 | double dstep; 39 | int dlen; 40 | }; 41 | 42 | /* 43 | * 1-d distance transform with quadratic distance: ax^2+bx 44 | * result is on a shifted, subsampled grid 45 | * used by eHshiftdt(). 46 | * 47 | * Profiling has shown this to be a slow point, so there has been some work to try and make this faster 48 | */ 49 | class dt1d { 50 | shiftData* const data; 51 | public: 52 | dt1d(shiftData* thread_data) : data(thread_data) {} 53 | 54 | void operator()(const tbb::blocked_range& range) const { 55 | for (size_t i=range.begin(); i!=range.end(); ++i) { 56 | run(i); 57 | } 58 | } 59 | 60 | void run(size_t i) const { 61 | shiftData currentData = data[i]; 62 | int* v = new int[currentData.slen]; 63 | double* z = new double[currentData.slen+1]; 64 | 65 | int* vBegin = v; 66 | double* zBegin = z; 67 | 68 | /* 69 | * v - locations of parabolas 70 | * z - locations of boundaries between parabolas 71 | * q - running index 72 | * s - intersection between two parabolas 73 | */ 74 | int q=0; 75 | *v = 0; 76 | *z = -EH_INF; 77 | *(z+1) = +EH_INF; 78 | double twoA = 2*currentData.a; 79 | 80 | for (q = 1; q < currentData.slen; ++q) { 81 | int qSstep = q*currentData.sstep; 82 | int qSquare = square(q); 83 | double s = ((currentData.src[qSstep]-currentData.src[*v*currentData.sstep])- 84 | currentData.b*(q-*v)+currentData.a*(qSquare-square(*v))) / (twoA*(q-*v)); 85 | while(s <= *z) { 86 | --v; 87 | --z; 88 | s = ((currentData.src[qSstep]-currentData.src[*v*currentData.sstep])- 89 | currentData.b*(q-*v)+currentData.a*(qSquare-square(*v))) / (twoA*(q-*v)); 90 | } 91 | ++v; 92 | ++z; 93 | *v = q; 94 | *z = s; 95 | *(z+1) = +EH_INF; 96 | } 97 | 98 | v = vBegin; 99 | z = zBegin; 100 | q = currentData.dshift; 101 | 102 | /* fill in values of distance transform */ 103 | for(int i=0; i < currentData.dlen; ++i) { 104 | while(*(z+1) < q) { 105 | ++z; 106 | ++v; 107 | } 108 | currentData.dst[i*currentData.sstep] = currentData.a*square(q-*v)+ 109 | currentData.b*(q-*v)+currentData.src[*v*currentData.sstep]; 110 | currentData.ptr[i*currentData.sstep] = *v; 111 | q += currentData.dstep; 112 | } 113 | 114 | delete[] zBegin; 115 | delete[] vBegin; 116 | } 117 | }; 118 | 119 | /* 120 | * NOTE: M, Ix, Iy should already be allocated before passed in, 121 | * they are then modified as output results 122 | * M, Ix, Iy, vals - column first order 123 | */ 124 | void eHshiftdt(double* M, int* Ix, int* Iy, 125 | int lenx, int leny, int offx, int offy, int dstep, 126 | const double* vals, int sizx, int sizy, 127 | const double* w, bool multiThreaded) { 128 | //double ax, double bx, double ay, double by) { 129 | /* 130 | * Calculation is performed as 1-d transforms in 2 steps 131 | * vals (sizy*sizx) => tmpM (leny*sizx) => M (leny*lenx) 132 | */ 133 | double* tmpM; 134 | int* tmpIy; 135 | /* negating to define a cost */ 136 | double ax = -w[0]; 137 | double bx = -w[1]; 138 | double ay = -w[2]; 139 | double by = -w[3]; 140 | 141 | tmpM = new double[leny*sizx]; 142 | tmpIy = new int[leny*sizx]; 143 | 144 | /* 1-d distance transforms on columns */ 145 | shiftData* data = new shiftData[sizx]; 146 | for(int x=0; x(0, sizx), dt1d(data)); 161 | } 162 | else { 163 | dt1d process(data); 164 | for (int i=0; i(0, sizy), dt1d(data)); 188 | } 189 | else { 190 | dt1d process(data); 191 | for (int i=0; i 2 | 4.0.0 3 | com.eharmony.modeling 4 | face-parts-service 5 | 2.0.2-SNAPSHOT 6 | Face Parts Service 7 | HTTP service for segmenting faces and getting fiducial markers 8 | 9 | 10 | scm:git:git@github.corp.eharmony.com:modeling/face-parts-service.git 11 | git@github.corp.eharmony.com:modeling/face-parts-service.git 12 | scm:git:git@github.corp.eharmony.com:modeling/face-parts-service.git 13 | 14 | 15 | 16 | 17 | false 18 | internal-releases 19 | eHarmony Releases Repo 20 | https://repository.corp.eharmony.com/nexus/content/repositories/internal-releases 21 | 22 | 23 | false 24 | internal-snapshots 25 | eHarmony Snapshot Repo 26 | https://repository.corp.eharmony.com/nexus/content/repositories/internal-snapshots 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-antrun-plugin 35 | 1.7 36 | 37 | 38 | qmake-main 39 | compile 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | run 49 | 50 | 51 | 52 | make-main 53 | compile 54 | 55 | 56 | 57 | 58 | 59 | 60 | run 61 | 62 | 63 | 64 | qmake-tests 65 | test 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | run 75 | 76 | 77 | 78 | make-tests 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | run 87 | 88 | 89 | 90 | run-tests 91 | test 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | run 102 | 103 | 104 | 105 | 106 | 107 | org.codehaus.mojo 108 | build-helper-maven-plugin 109 | 1.8 110 | 111 | 112 | attach-binary 113 | package 114 | 115 | attach-artifact 116 | 117 | 118 | 119 | 120 | target/classes/face-parts-service 121 | exe 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-release-plugin 131 | 2.0 132 | 133 | 134 | org.apache.maven.scm 135 | maven-scm-provider-gitexe 136 | 1.3 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfHttpServer/src/httprequest.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef HTTPREQUEST_H 7 | #define HTTPREQUEST_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /** 18 | This object represents a single HTTP request. It reads the request 19 | from a TCP socket and provides getters for the individual parts 20 | of the request. 21 |

22 | The follwing config settings are required: 23 |

 24 |   maxRequestSize=16000
 25 |   maxMultiPartSize=1000000
 26 |   
27 |

28 | MaxRequestSize is the maximum size of a HTTP request. In case of 29 | multipart/form-data requests (also known as file-upload), the maximum 30 | size of the body must not exceed maxMultiPartSize. 31 | The body is always a little larger than the file itself. 32 | */ 33 | 34 | class HttpRequest { 35 | Q_DISABLE_COPY(HttpRequest) 36 | friend class HttpSessionStore; 37 | public: 38 | 39 | /** Values for getStatus() */ 40 | enum RequestStatus {waitForRequest, waitForHeader, waitForBody, complete, abort}; 41 | 42 | /** 43 | Constructor. 44 | @param settings Configuration settings 45 | */ 46 | HttpRequest(QSettings* settings); 47 | 48 | /** 49 | Destructor. 50 | */ 51 | virtual ~HttpRequest(); 52 | 53 | /** 54 | Read the request from a socket. This method must be called repeatedly 55 | until the status is RequestStatus::complete or RequestStatus::abort. 56 | @param socket Source of the data 57 | */ 58 | void readFromSocket(QTcpSocket& socket); 59 | 60 | /** 61 | Get the status of this reqeust. 62 | @see RequestStatus 63 | */ 64 | RequestStatus getStatus() const; 65 | 66 | /** Get the method of the HTTP request (e.g. "GET") */ 67 | QByteArray getMethod() const; 68 | 69 | /** Get the decoded path of the HTPP request (e.g. "/index.html") */ 70 | QByteArray getPath() const; 71 | 72 | /** Set the decoded path of the HTTP request */ 73 | void setPath(const QByteArray& path); 74 | 75 | /** Get the version of the HTPP request (e.g. "HTTP/1.1") */ 76 | QByteArray getVersion() const; 77 | 78 | /** 79 | Get the value of a HTTP request header. 80 | @param name Name of the header 81 | @return If the header occurs multiple times, only the last 82 | one is returned. 83 | */ 84 | QByteArray getHeader(const QByteArray& name) const; 85 | 86 | /** 87 | Get the values of a HTTP request header. 88 | @param name Name of the header 89 | */ 90 | QList getHeaders(const QByteArray& name) const; 91 | 92 | /** Get all HTTP request headers */ 93 | QMultiMap getHeaderMap() const; 94 | 95 | /** 96 | Get the value of a HTTP request parameter. 97 | @param name Name of the parameter 98 | @return If the parameter occurs multiple times, only the last 99 | one is returned. 100 | */ 101 | QByteArray getParameter(const QByteArray& name) const; 102 | 103 | /** 104 | Get the values of a HTTP request parameter. 105 | @param name Name of the parameter 106 | */ 107 | QList getParameters(const QByteArray& name) const; 108 | 109 | /** Get all HTTP request parameters */ 110 | QMultiMap getParameterMap() const; 111 | 112 | /** Get the HTTP request body */ 113 | QByteArray getBody() const; 114 | 115 | /** 116 | Decode an URL parameter. 117 | E.g. replace "%23" by '#' and replace '+' by ' '. 118 | @param source The url encoded strings 119 | @see QUrl::toPercentEncoding for the reverse direction 120 | */ 121 | static QByteArray urlDecode(const QByteArray source); 122 | 123 | /** 124 | Get an uploaded file. The file is already open. It will 125 | be closed and deleted by the destructor of this HttpRequest 126 | object (after processing the request). 127 |

128 | For uploaded files, the method getParameters() returns 129 | the original fileName as provided by the calling web browser. 130 | */ 131 | QTemporaryFile* getUploadedFile(const QByteArray fieldName); 132 | 133 | /** 134 | Get the value of a cookie 135 | @param name Name of the cookie 136 | */ 137 | QByteArray getCookie(const QByteArray& name) const; 138 | 139 | /** Get the map of cookies */ 140 | QMap& getCookieMap(); 141 | 142 | private: 143 | 144 | /** Request headers */ 145 | QMultiMap headers; 146 | 147 | /** Parameters of the request */ 148 | QMultiMap parameters; 149 | 150 | /** Uploaded files of the request, key is the field name. */ 151 | QMap uploadedFiles; 152 | 153 | /** Received cookies */ 154 | QMap cookies; 155 | 156 | /** Storage for raw body data */ 157 | QByteArray bodyData; 158 | 159 | /** Request method */ 160 | QByteArray method; 161 | 162 | /** Request path (in raw encoded format) */ 163 | QByteArray path; 164 | 165 | /** Request protocol version */ 166 | QByteArray version; 167 | 168 | /** 169 | Status of this request. 170 | @see RequestStatus 171 | */ 172 | RequestStatus status; 173 | 174 | /** Maximum size of requests in bytes. */ 175 | int maxSize; 176 | 177 | /** Maximum allowed size of multipart forms in bytes. */ 178 | int maxMultiPartSize; 179 | 180 | /** Current size */ 181 | int currentSize; 182 | 183 | /** Expected size of body */ 184 | int expectedBodySize; 185 | 186 | /** Name of the current header, or empty if no header is being processed */ 187 | QByteArray currentHeader; 188 | 189 | /** Boundary of multipart/form-data body. Empty if there is no such header */ 190 | QByteArray boundary; 191 | 192 | /** Temp file, that is used to store the multipart/form-data body */ 193 | QTemporaryFile tempFile; 194 | 195 | /** Parset he multipart body, that has been stored in the temp file. */ 196 | void parseMultiPartFile(); 197 | 198 | /** Sub-procedure of readFromSocket(), read the first line of a request. */ 199 | void readRequest(QTcpSocket& socket); 200 | 201 | /** Sub-procedure of readFromSocket(), read header lines. */ 202 | void readHeader(QTcpSocket& socket); 203 | 204 | /** Sub-procedure of readFromSocket(), read the request body. */ 205 | void readBody(QTcpSocket& socket); 206 | 207 | /** Sub-procedure of readFromSocket(), extract and decode request parameters. */ 208 | void decodeRequestParams(); 209 | 210 | /** Sub-procedure of readFromSocket(), extract cookies from headers */ 211 | void extractCookies(); 212 | 213 | }; 214 | 215 | #endif // HTTPREQUEST_H 216 | -------------------------------------------------------------------------------- /src/main/c++/detect-face/eHimageFeature.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eHimageFeature.cpp 3 | * 4 | * Hang Su 5 | * 2012-08 @ eH 6 | */ 7 | #include 8 | #include "eHimage.h" 9 | #include "eHmatrix.h" 10 | 11 | /* small value, used to avoid division by zero */ 12 | #define eps 0.0001 13 | 14 | /* unit vectors used to compute gradient orientation */ 15 | double uu[9] = {1.0000, 16 | 0.9397, 17 | 0.7660, 18 | 0.500, 19 | 0.1736, 20 | -0.1736, 21 | -0.5000, 22 | -0.7660, 23 | -0.9397}; 24 | double vv[9] = {0.0000, 25 | 0.3420, 26 | 0.6428, 27 | 0.8660, 28 | 0.9848, 29 | 0.9848, 30 | 0.8660, 31 | 0.6428, 32 | 0.3420}; 33 | 34 | static inline double min(double x, double y) { return (x <= y ? x : y); } 35 | 36 | static inline int min(int x, int y) { return (x <= y ? x : y); } 37 | static inline int max(int x, int y) { return (x <= y ? y : x); } 38 | 39 | static inline int round2int(double x) { return (int)(x+0.5);} 40 | 41 | /* 42 | * entry point, 43 | * takes a double color image and a bin size 44 | * returns HOG features 45 | */ 46 | mat3d_t* eHhog(const image_t* img, int sbin) { 47 | 48 | /* memory for caching orientation histograms & their norms */ 49 | int dims[2] = {img->sizy, img->sizx}; 50 | int blocks[2]; 51 | blocks[0] = (int)round2int((double)dims[0]/(double)sbin); 52 | blocks[1] = (int)round2int((double)dims[1]/(double)sbin); 53 | double *hist = new double[blocks[0]*blocks[1]*18]; 54 | double *norm = new double[blocks[0]*blocks[1]]; 55 | for(int i=0;ich[2] + min(x, dims[1]-2)*dims[0] + min(y, dims[0]-2); 75 | double dy = *(s+1) - *(s-1); 76 | double dx = *(s+dims[0]) - *(s-dims[0]); 77 | double v = dx*dx + dy*dy; 78 | 79 | /* second color channel */ 80 | s = img->ch[1] + min(x, dims[1]-2)*dims[0] + min(y,dims[0]-2); 81 | double dy2 = *(s+1) - *(s-1); 82 | double dx2 = *(s+dims[0]) - *(s-dims[0]); 83 | double v2 = dx2*dx2 + dy2*dy2; 84 | 85 | /* third color channel */ 86 | s = img->ch[0] + min(x, dims[1]-2)*dims[0] + min(y,dims[0]-2); 87 | double dy3 = *(s+1) - *(s-1); 88 | double dx3 = *(s+dims[0]) - *(s-dims[0]); 89 | double v3 = dx3*dx3 + dy3*dy3; 90 | 91 | /* pick channel with strongest gradient */ 92 | if (v2 > v) { 93 | v = v2; 94 | dx = dx2; 95 | dy = dy2; 96 | } 97 | if (v3 > v) { 98 | v = v3; 99 | dx = dx3; 100 | dy = dy3; 101 | } 102 | 103 | /* snap to one of 18 orientations */ 104 | double best_dot = 0; 105 | int best_o = 0; 106 | for (int o = 0; o < 9; o++) { 107 | double dot = uu[o]*dx + vv[o]*dy; 108 | if (dot > best_dot) { 109 | best_dot = dot; 110 | best_o = o; 111 | } else if (-dot > best_dot) { 112 | best_dot = -dot; 113 | best_o = o+9; 114 | } 115 | } 116 | 117 | /* add to 4 histograms around pixel using linear interpolation */ 118 | double xp = ((double)x+0.5)/(double)sbin - 0.5; 119 | double yp = ((double)y+0.5)/(double)sbin - 0.5; 120 | int ixp = (int)floor(xp); 121 | int iyp = (int)floor(yp); 122 | double vx0 = xp-ixp; 123 | double vy0 = yp-iyp; 124 | double vx1 = 1.0-vx0; 125 | double vy1 = 1.0-vy0; 126 | v = sqrt(v); 127 | 128 | if (ixp >= 0 && iyp >= 0) { 129 | *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 130 | vx1*vy1*v; 131 | } 132 | 133 | if (ixp+1 < blocks[1] && iyp >= 0) { 134 | *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 135 | vx0*vy1*v; 136 | } 137 | 138 | if (ixp >= 0 && iyp+1 < blocks[0]) { 139 | *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 140 | vx1*vy0*v; 141 | } 142 | 143 | if (ixp+1 < blocks[1] && iyp+1 < blocks[0]) { 144 | *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 145 | vx0*vy0*v; 146 | } 147 | } 148 | } 149 | 150 | /* compute energy in each block by summing over orientations */ 151 | for (int o = 0; o < 9; o++) { 152 | double *src1 = hist + o*blocks[0]*blocks[1]; 153 | double *src2 = hist + (o+9)*blocks[0]*blocks[1]; 154 | double *dst = norm; 155 | double *end = norm + blocks[1]*blocks[0]; 156 | while (dst < end) { 157 | *(dst++) += (*src1 + *src2) * (*src1 + *src2); 158 | src1++; 159 | src2++; 160 | } 161 | } 162 | 163 | /* compute features */ 164 | for (int x = 0; x < out[1]; x++) { 165 | for (int y = 0; y < out[0]; y++) { 166 | double *dst = feat->vals + x*out[0] + y; 167 | double *src, *p, n1, n2, n3, n4; 168 | 169 | p = norm + (x+1)*blocks[0] + y+1; 170 | n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 171 | p = norm + (x+1)*blocks[0] + y; 172 | n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 173 | p = norm + x*blocks[0] + y+1; 174 | n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 175 | p = norm + x*blocks[0] + y; 176 | n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 177 | 178 | double t1 = 0; 179 | double t2 = 0; 180 | double t3 = 0; 181 | double t4 = 0; 182 | 183 | /* contrast-sensitive features */ 184 | src = hist + (x+1)*blocks[0] + (y+1); 185 | for (int o = 0; o < 18; o++) { 186 | double h1 = min(*src * n1, 0.2); 187 | double h2 = min(*src * n2, 0.2); 188 | double h3 = min(*src * n3, 0.2); 189 | double h4 = min(*src * n4, 0.2); 190 | *dst = 0.5 * (h1 + h2 + h3 + h4); 191 | t1 += h1; 192 | t2 += h2; 193 | t3 += h3; 194 | t4 += h4; 195 | dst += out[0]*out[1]; 196 | src += blocks[0]*blocks[1]; 197 | } 198 | 199 | /* contrast-insensitive features */ 200 | src = hist + (x+1)*blocks[0] + (y+1); 201 | for (int o = 0; o < 9; o++) { 202 | double sum = *src + *(src + 9*blocks[0]*blocks[1]); 203 | double h1 = min(sum * n1, 0.2); 204 | double h2 = min(sum * n2, 0.2); 205 | double h3 = min(sum * n3, 0.2); 206 | double h4 = min(sum * n4, 0.2); 207 | *dst = 0.5 * (h1 + h2 + h3 + h4); 208 | dst += out[0]*out[1]; 209 | src += blocks[0]*blocks[1]; 210 | } 211 | 212 | /* texture features */ 213 | *dst = 0.2357 * t1; 214 | dst += out[0]*out[1]; 215 | *dst = 0.2357 * t2; 216 | dst += out[0]*out[1]; 217 | *dst = 0.2357 * t3; 218 | dst += out[0]*out[1]; 219 | *dst = 0.2357 * t4; 220 | 221 | /* truncation feature */ 222 | dst += out[0]*out[1]; 223 | *dst = 0; 224 | } 225 | } 226 | 227 | delete[] hist; 228 | delete[] norm; 229 | return feat; 230 | } 231 | 232 | -------------------------------------------------------------------------------- /src/main/c++/qtwebapp/bfLogging/src/logger.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file 3 | @author Stefan Frings 4 | */ 5 | 6 | #ifndef LOGGER_H 7 | #define LOGGER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "logmessage.h" 15 | 16 | /** 17 | Decorates and writes log messages to the console, stderr. 18 |

19 | The decorator uses a predefined msgFormat string to enrich log messages 20 | with additional information (e.g. timestamp). 21 |

22 | The msgFormat string and also the message text may contain additional 23 | variable names in the form {name} that are filled by values 24 | taken from a static thread local dictionary. 25 |

26 | The logger keeps a configurable number of messages in a ring-buffer. 27 | A log message with a severity >= minLevel flushes the buffer, 28 | so the stored messages get written out. If the buffer is disabled, then 29 | only messages with severity >= minLevel are written out. 30 |

31 | If you enable the buffer and use minLevel=2, then the application logs 32 | only errors together with some buffered debug messages. But as long no 33 | error occurs, nothing gets written out. 34 |

35 | Each thread has it's own buffer. 36 |

37 | The logger can be registered to handle messages from 38 | the static global functions qDebug(), qWarning(), qCritical() and qFatal(). 39 | 40 | @see set() describes how to set logger variables 41 | @see LogMessage for a description of the message decoration. 42 | @warning You should prefer a derived class, for example FileLogger, 43 | because logging to the console is less useful. 44 | */ 45 | 46 | class Logger : public QObject { 47 | Q_OBJECT 48 | Q_DISABLE_COPY(Logger) 49 | public: 50 | 51 | /** 52 | Constructor. 53 | Uses the same defaults as the other constructor. 54 | @param parent Parent object 55 | */ 56 | Logger(QObject* parent); 57 | 58 | 59 | /** 60 | Constructor. 61 | @param msgFormat Format of the decoration, e.g. "{timestamp} {type} thread={thread}: {msg}" 62 | @param timestampFormat Format of timestamp, e.g. "dd.MM.yyyy hh:mm:ss.zzz" 63 | @param minLevel Minimum severity that genertes an output (0=debug, 1=warning, 2=critical, 3=fatal). 64 | @param bufferSize Size of the backtrace buffer, number of messages per thread. 0=disabled. 65 | @param parent Parent object 66 | @see LogMessage for a description of the message decoration. 67 | */ 68 | Logger(const QString msgFormat="{timestamp} {type} {msg}", const QString timestampFormat="dd.MM.yyyy hh:mm:ss.zzz", const QtMsgType minLevel=QtDebugMsg, const int bufferSize=0, QObject* parent = 0); 69 | 70 | /** Destructor */ 71 | virtual ~Logger(); 72 | 73 | /** 74 | Decorate and log the message, if type>=minLevel. 75 | This method is thread safe. 76 | @param type Message type (level) 77 | @param message Message text 78 | @param file Name of the source file where the message was generated (usually filled with the macro __FILE__) 79 | @param function Name of the function where the message was generated (usually filled with the macro __LINE__) 80 | @param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__) 81 | @see LogMessage for a description of the message decoration. 82 | */ 83 | virtual void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0); 84 | 85 | /** 86 | Installs this logger as the default message handler, so it 87 | can be used through the global static logging functions (e.g. qDebug()). 88 | */ 89 | void installMsgHandler(); 90 | 91 | /** 92 | Sets a thread-local variable that may be used to decorate log messages. 93 | This method is thread safe. 94 | @param name Name of the variable 95 | @param value Value of the variable 96 | */ 97 | static void set(const QString& name, const QString& value); 98 | 99 | /** 100 | Clear the thread-local data of the current thread. 101 | @param buffer Whether to clear the backtrace buffer 102 | @param variables Whether to clear the log variables 103 | */ 104 | static void clear(const bool buffer=true, const bool variables=true); 105 | 106 | protected: 107 | 108 | /** Format string for message decoration */ 109 | QString msgFormat; 110 | 111 | /** Format string of timestamps */ 112 | QString timestampFormat; 113 | 114 | /** Minimum level of message types that are written out */ 115 | QtMsgType minLevel; 116 | 117 | /** Size of backtrace buffer, number of messages per thread. 0=disabled */ 118 | int bufferSize; 119 | 120 | /** Used to synchronize access to the static members */ 121 | static QMutex mutex; 122 | 123 | /** 124 | Decorate and write a log message to stderr. Override this method 125 | to provide a different output medium. 126 | */ 127 | virtual void write(const LogMessage* logMessage); 128 | 129 | private: 130 | 131 | /** Pointer to the default logger, used by msgHandler() */ 132 | static Logger* defaultLogger; 133 | 134 | /** 135 | Message Handler for the global static logging functions (e.g. qDebug()). 136 | Forward calls to the default logger. 137 |

138 | In case of a fatal message, the program will abort. 139 | Variables in the in the message are replaced by their values. 140 | This method is thread safe. 141 | @param type Message type (level) 142 | @param message Message text 143 | @param file Name of the source file where the message was generated (usually filled with the macro __FILE__) 144 | @param function Name of the function where the message was generated (usually filled with the macro __LINE__) 145 | @param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__) 146 | */ 147 | static void msgHandler(const QtMsgType type, const QString &message, const QString &file="", const QString &function="", const int line=0); 148 | 149 | 150 | #if QT_VERSION >= 0x050000 151 | 152 | /** 153 | Wrapper for QT version 5. 154 | @param type Message type (level) 155 | @param context Message context 156 | @param message Message text 157 | @see msgHandler() 158 | */ 159 | static void msgHandler5(const QtMsgType type, const QMessageLogContext& context, const QString &message); 160 | 161 | #else 162 | 163 | /** 164 | Wrapper for QT version 4. 165 | @param type Message type (level) 166 | @param message Message text 167 | @see msgHandler() 168 | */ 169 | static void msgHandler4(const QtMsgType type, const char * message); 170 | 171 | #endif 172 | 173 | /** Thread local variables to be used in log messages */ 174 | static QThreadStorage*> logVars; 175 | 176 | /** Thread local backtrace buffers */ 177 | static QThreadStorage*> buffers; 178 | 179 | }; 180 | 181 | #endif // LOGGER_H 182 | -------------------------------------------------------------------------------- /src/test/resources/1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "face": { 4 | "x1": 218.263186, 5 | "x2": 435.026371, 6 | "y1": 118.225892, 7 | "y2": 334.989078 8 | }, 9 | "pose": 0, 10 | "model": "frontal", 11 | "parts": { 12 | "nostrils right to left": [ 13 | { 14 | "x": 331.191925, 15 | "y": 231.154633, 16 | "num": 0 17 | }, 18 | { 19 | "x": 322.097626, 20 | "y": 231.154633, 21 | "num": 1 22 | }, 23 | { 24 | "x": 322.097626, 25 | "y": 222.060333, 26 | "num": 2 27 | }, 28 | { 29 | "x": 340.286224, 30 | "y": 231.154633, 31 | "num": 3 32 | }, 33 | { 34 | "x": 349.380524, 35 | "y": 231.154633, 36 | "num": 4 37 | } 38 | ], 39 | "nose bottom to top": [ 40 | { 41 | "x": 331.191925, 42 | "y": 212.966034, 43 | "num": 5 44 | }, 45 | { 46 | "x": 331.191925, 47 | "y": 194.777435, 48 | "num": 6 49 | }, 50 | { 51 | "x": 331.191925, 52 | "y": 176.588837, 53 | "num": 7 54 | }, 55 | { 56 | "x": 331.191925, 57 | "y": 167.494537, 58 | "num": 8 59 | } 60 | ], 61 | "right eye left to right": [ 62 | { 63 | "x": 303.909027, 64 | "y": 167.494537, 65 | "num": 9 66 | }, 67 | { 68 | "x": 303.909027, 69 | "y": 167.494537, 70 | "num": 10 71 | }, 72 | { 73 | "x": 294.814728, 74 | "y": 167.494537, 75 | "num": 11 76 | }, 77 | { 78 | "x": 294.814728, 79 | "y": 167.494537, 80 | "num": 12 81 | }, 82 | { 83 | "x": 294.814728, 84 | "y": 167.494537, 85 | "num": 13 86 | }, 87 | { 88 | "x": 285.720428, 89 | "y": 167.494537, 90 | "num": 14 91 | } 92 | ], 93 | "right eyebrow right to left": [ 94 | { 95 | "x": 267.531830, 96 | "y": 158.400238, 97 | "num": 15 98 | }, 99 | { 100 | "x": 276.626129, 101 | "y": 149.305939, 102 | "num": 16 103 | }, 104 | { 105 | "x": 285.720428, 106 | "y": 140.211639, 107 | "num": 17 108 | }, 109 | { 110 | "x": 303.909027, 111 | "y": 149.305939, 112 | "num": 18 113 | }, 114 | { 115 | "x": 322.097626, 116 | "y": 149.305939, 117 | "num": 19 118 | } 119 | ], 120 | "left eye right to left": [ 121 | { 122 | "x": 349.380524, 123 | "y": 167.494537, 124 | "num": 20 125 | }, 126 | { 127 | "x": 358.474823, 128 | "y": 167.494537, 129 | "num": 21 130 | }, 131 | { 132 | "x": 367.569122, 133 | "y": 167.494537, 134 | "num": 22 135 | }, 136 | { 137 | "x": 358.474823, 138 | "y": 167.494537, 139 | "num": 23 140 | }, 141 | { 142 | "x": 367.569122, 143 | "y": 167.494537, 144 | "num": 24 145 | }, 146 | { 147 | "x": 376.663422, 148 | "y": 167.494537, 149 | "num": 25 150 | } 151 | ], 152 | "left eyebrow left to right": [ 153 | { 154 | "x": 394.852020, 155 | "y": 149.305939, 156 | "num": 26 157 | }, 158 | { 159 | "x": 385.757721, 160 | "y": 149.305939, 161 | "num": 27 162 | }, 163 | { 164 | "x": 367.569122, 165 | "y": 149.305939, 166 | "num": 28 167 | }, 168 | { 169 | "x": 358.474823, 170 | "y": 149.305939, 171 | "num": 29 172 | }, 173 | { 174 | "x": 340.286224, 175 | "y": 149.305939, 176 | "num": 30 177 | } 178 | ], 179 | "upper lip right to left": [ 180 | { 181 | "x": 331.191925, 182 | "y": 240.248932, 183 | "num": 31 184 | }, 185 | { 186 | "x": 322.097626, 187 | "y": 240.248932, 188 | "num": 32 189 | }, 190 | { 191 | "x": 313.003326, 192 | "y": 249.343231, 193 | "num": 33 194 | }, 195 | { 196 | "x": 303.909027, 197 | "y": 258.437531, 198 | "num": 34 199 | }, 200 | { 201 | "x": 313.003326, 202 | "y": 258.437531, 203 | "num": 35 204 | }, 205 | { 206 | "x": 322.097626, 207 | "y": 249.343231, 208 | "num": 36 209 | }, 210 | { 211 | "x": 331.191925, 212 | "y": 249.343231, 213 | "num": 37 214 | }, 215 | { 216 | "x": 349.380524, 217 | "y": 240.248932, 218 | "num": 38 219 | }, 220 | { 221 | "x": 358.474823, 222 | "y": 249.343231, 223 | "num": 39 224 | }, 225 | { 226 | "x": 367.569122, 227 | "y": 249.343231, 228 | "num": 40 229 | } 230 | ], 231 | "lower lip left to right": [ 232 | { 233 | "x": 358.474823, 234 | "y": 249.343231, 235 | "num": 41 236 | }, 237 | { 238 | "x": 349.380524, 239 | "y": 249.343231, 240 | "num": 42 241 | }, 242 | { 243 | "x": 358.474823, 244 | "y": 258.437531, 245 | "num": 43 246 | }, 247 | { 248 | "x": 349.380524, 249 | "y": 249.343231, 250 | "num": 44 251 | }, 252 | { 253 | "x": 349.380524, 254 | "y": 267.531830, 255 | "num": 45 256 | }, 257 | { 258 | "x": 331.191925, 259 | "y": 258.437531, 260 | "num": 46 261 | }, 262 | { 263 | "x": 322.097626, 264 | "y": 267.531830, 265 | "num": 47 266 | }, 267 | { 268 | "x": 322.097626, 269 | "y": 249.343231, 270 | "num": 48 271 | }, 272 | { 273 | "x": 313.003326, 274 | "y": 258.437531, 275 | "num": 49 276 | }, 277 | { 278 | "x": 331.191925, 279 | "y": 267.531830, 280 | "num": 50 281 | } 282 | ], 283 | "right jaw bottom to top": [ 284 | { 285 | "x": 340.286224, 286 | "y": 313.003326, 287 | "num": 51 288 | }, 289 | { 290 | "x": 322.097626, 291 | "y": 303.909027, 292 | "num": 52 293 | }, 294 | { 295 | "x": 303.909027, 296 | "y": 294.814728, 297 | "num": 53 298 | }, 299 | { 300 | "x": 285.720428, 301 | "y": 276.626129, 302 | "num": 54 303 | }, 304 | { 305 | "x": 267.531830, 306 | "y": 258.437531, 307 | "num": 55 308 | }, 309 | { 310 | "x": 258.437531, 311 | "y": 231.154633, 312 | "num": 56 313 | }, 314 | { 315 | "x": 249.343231, 316 | "y": 203.871735, 317 | "num": 57 318 | }, 319 | { 320 | "x": 240.248932, 321 | "y": 176.588837, 322 | "num": 58 323 | }, 324 | { 325 | "x": 240.248932, 326 | "y": 149.305939, 327 | "num": 59 328 | } 329 | ], 330 | "left jaw bottom to top": [ 331 | { 332 | "x": 358.474823, 333 | "y": 303.909027, 334 | "num": 60 335 | }, 336 | { 337 | "x": 376.663422, 338 | "y": 294.814728, 339 | "num": 61 340 | }, 341 | { 342 | "x": 385.757721, 343 | "y": 285.720428, 344 | "num": 62 345 | }, 346 | { 347 | "x": 394.852020, 348 | "y": 258.437531, 349 | "num": 63 350 | }, 351 | { 352 | "x": 403.946320, 353 | "y": 240.248932, 354 | "num": 64 355 | }, 356 | { 357 | "x": 403.946320, 358 | "y": 222.060333, 359 | "num": 65 360 | }, 361 | { 362 | "x": 413.040619, 363 | "y": 194.777435, 364 | "num": 66 365 | }, 366 | { 367 | "x": 413.040619, 368 | "y": 176.588837, 369 | "num": 67 370 | } 371 | ] 372 | } 373 | } 374 | ] -------------------------------------------------------------------------------- /src/test/resources/10156670_4.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "face": { 4 | "x1": 478.488155, 5 | "x2": 544.372771, 6 | "y1": 215.320800, 7 | "y2": 283.860926 8 | }, 9 | "pose": -15, 10 | "model": "frontal", 11 | "parts": { 12 | "nostrils right to left": [ 13 | { 14 | "x": 514.085999, 15 | "y": 250.918625, 16 | "num": 0 17 | }, 18 | { 19 | "x": 511.430450, 20 | "y": 250.918625, 21 | "num": 1 22 | }, 23 | { 24 | "x": 508.774963, 25 | "y": 248.263107, 26 | "num": 2 27 | }, 28 | { 29 | "x": 516.741455, 30 | "y": 250.918625, 31 | "num": 3 32 | }, 33 | { 34 | "x": 519.396973, 35 | "y": 250.918625, 36 | "num": 4 37 | } 38 | ], 39 | "nose bottom to top": [ 40 | { 41 | "x": 511.430450, 42 | "y": 242.952087, 43 | "num": 5 44 | }, 45 | { 46 | "x": 511.430450, 47 | "y": 240.296585, 48 | "num": 6 49 | }, 50 | { 51 | "x": 511.430450, 52 | "y": 237.641068, 53 | "num": 7 54 | }, 55 | { 56 | "x": 511.430450, 57 | "y": 234.985565, 58 | "num": 8 59 | } 60 | ], 61 | "right eye left to right": [ 62 | { 63 | "x": 506.119446, 64 | "y": 234.985565, 65 | "num": 9 66 | }, 67 | { 68 | "x": 506.119446, 69 | "y": 234.985565, 70 | "num": 10 71 | }, 72 | { 73 | "x": 503.463928, 74 | "y": 234.985565, 75 | "num": 11 76 | }, 77 | { 78 | "x": 503.463928, 79 | "y": 232.330048, 80 | "num": 12 81 | }, 82 | { 83 | "x": 500.808411, 84 | "y": 232.330048, 85 | "num": 13 86 | }, 87 | { 88 | "x": 495.497406, 89 | "y": 234.985565, 90 | "num": 14 91 | } 92 | ], 93 | "right eyebrow right to left": [ 94 | { 95 | "x": 490.186371, 96 | "y": 227.019028, 97 | "num": 15 98 | }, 99 | { 100 | "x": 492.841888, 101 | "y": 224.363525, 102 | "num": 16 103 | }, 104 | { 105 | "x": 495.497406, 106 | "y": 224.363525, 107 | "num": 17 108 | }, 109 | { 110 | "x": 498.152924, 111 | "y": 221.708008, 112 | "num": 18 113 | }, 114 | { 115 | "x": 500.808411, 116 | "y": 221.708008, 117 | "num": 19 118 | } 119 | ], 120 | "left eye right to left": [ 121 | { 122 | "x": 519.396973, 123 | "y": 234.985565, 124 | "num": 20 125 | }, 126 | { 127 | "x": 522.052490, 128 | "y": 234.985565, 129 | "num": 21 130 | }, 131 | { 132 | "x": 524.708008, 133 | "y": 234.985565, 134 | "num": 22 135 | }, 136 | { 137 | "x": 522.052490, 138 | "y": 232.330048, 139 | "num": 23 140 | }, 141 | { 142 | "x": 524.708008, 143 | "y": 232.330048, 144 | "num": 24 145 | }, 146 | { 147 | "x": 527.363525, 148 | "y": 234.985565, 149 | "num": 25 150 | } 151 | ], 152 | "left eyebrow left to right": [ 153 | { 154 | "x": 535.330078, 155 | "y": 229.674545, 156 | "num": 26 157 | }, 158 | { 159 | "x": 532.674561, 160 | "y": 227.019028, 161 | "num": 27 162 | }, 163 | { 164 | "x": 527.363525, 165 | "y": 227.019028, 166 | "num": 28 167 | }, 168 | { 169 | "x": 522.052490, 170 | "y": 227.019028, 171 | "num": 29 172 | }, 173 | { 174 | "x": 516.741455, 175 | "y": 227.019028, 176 | "num": 30 177 | } 178 | ], 179 | "upper lip right to left": [ 180 | { 181 | "x": 514.085999, 182 | "y": 253.574127, 183 | "num": 31 184 | }, 185 | { 186 | "x": 511.430450, 187 | "y": 253.574127, 188 | "num": 32 189 | }, 190 | { 191 | "x": 506.119446, 192 | "y": 256.229645, 193 | "num": 33 194 | }, 195 | { 196 | "x": 503.463928, 197 | "y": 258.885132, 198 | "num": 34 199 | }, 200 | { 201 | "x": 506.119446, 202 | "y": 258.885132, 203 | "num": 35 204 | }, 205 | { 206 | "x": 511.430450, 207 | "y": 256.229645, 208 | "num": 36 209 | }, 210 | { 211 | "x": 514.085999, 212 | "y": 256.229645, 213 | "num": 37 214 | }, 215 | { 216 | "x": 519.396973, 217 | "y": 253.574127, 218 | "num": 38 219 | }, 220 | { 221 | "x": 522.052490, 222 | "y": 256.229645, 223 | "num": 39 224 | }, 225 | { 226 | "x": 524.708008, 227 | "y": 258.885132, 228 | "num": 40 229 | } 230 | ], 231 | "lower lip left to right": [ 232 | { 233 | "x": 522.052490, 234 | "y": 258.885132, 235 | "num": 41 236 | }, 237 | { 238 | "x": 519.396973, 239 | "y": 256.229645, 240 | "num": 42 241 | }, 242 | { 243 | "x": 522.052490, 244 | "y": 261.540649, 245 | "num": 43 246 | }, 247 | { 248 | "x": 514.085999, 249 | "y": 256.229645, 250 | "num": 44 251 | }, 252 | { 253 | "x": 511.430450, 254 | "y": 258.885132, 255 | "num": 45 256 | }, 257 | { 258 | "x": 506.119446, 259 | "y": 256.229645, 260 | "num": 46 261 | }, 262 | { 263 | "x": 503.463928, 264 | "y": 261.540649, 265 | "num": 47 266 | }, 267 | { 268 | "x": 503.463928, 269 | "y": 256.229645, 270 | "num": 48 271 | }, 272 | { 273 | "x": 500.808411, 274 | "y": 261.540649, 275 | "num": 49 276 | }, 277 | { 278 | "x": 506.119446, 279 | "y": 261.540649, 280 | "num": 50 281 | } 282 | ], 283 | "right jaw bottom to top": [ 284 | { 285 | "x": 506.119446, 286 | "y": 277.473724, 287 | "num": 51 288 | }, 289 | { 290 | "x": 500.808411, 291 | "y": 274.818207, 292 | "num": 52 293 | }, 294 | { 295 | "x": 495.497406, 296 | "y": 272.162689, 297 | "num": 53 298 | }, 299 | { 300 | "x": 492.841888, 301 | "y": 266.851685, 302 | "num": 54 303 | }, 304 | { 305 | "x": 490.186371, 306 | "y": 264.196167, 307 | "num": 55 308 | }, 309 | { 310 | "x": 490.186371, 311 | "y": 258.885132, 312 | "num": 56 313 | }, 314 | { 315 | "x": 487.530884, 316 | "y": 253.574127, 317 | "num": 57 318 | }, 319 | { 320 | "x": 484.875366, 321 | "y": 248.263107, 322 | "num": 58 323 | }, 324 | { 325 | "x": 484.875366, 326 | "y": 242.952087, 327 | "num": 59 328 | } 329 | ], 330 | "left jaw bottom to top": [ 331 | { 332 | "x": 514.085999, 333 | "y": 277.473724, 334 | "num": 60 335 | }, 336 | { 337 | "x": 519.396973, 338 | "y": 277.473724, 339 | "num": 61 340 | }, 341 | { 342 | "x": 527.363525, 343 | "y": 274.818207, 344 | "num": 62 345 | }, 346 | { 347 | "x": 530.019043, 348 | "y": 272.162689, 349 | "num": 63 350 | }, 351 | { 352 | "x": 535.330078, 353 | "y": 266.851685, 354 | "num": 64 355 | }, 356 | { 357 | "x": 535.330078, 358 | "y": 261.540649, 359 | "num": 65 360 | }, 361 | { 362 | "x": 537.985535, 363 | "y": 253.574127, 364 | "num": 66 365 | }, 366 | { 367 | "x": 537.985535, 368 | "y": 248.263107, 369 | "num": 67 370 | } 371 | ] 372 | } 373 | } 374 | ] 375 | --------------------------------------------------------------------------------