Skip to content
Permalink
Browse files

initial commit

  • Loading branch information...
hrastnik committed Mar 31, 2016
1 parent 553bf45 commit 2f7e2d32192a6fe62a642c7f1b7f8b83bda58d46
Showing with 809 additions and 2 deletions.
  1. +222 −0 FaceDetectorAndTracker.cpp
  2. +97 −0 FaceDetectorAndTracker.h
  3. +313 −0 FaceSwapper.cpp
  4. +97 −0 FaceSwapper.h
  5. +29 −2 README.md
  6. BIN images/after.jpg
  7. BIN images/before.jpg
  8. +51 −0 main.cpp
@@ -0,0 +1,222 @@
#include "FaceDetectorAndTracker.h"

#include <opencv2/core.hpp>
#include <opencv2/video.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <iostream>

FaceDetectorAndTracker::FaceDetectorAndTracker(const std::string cascadeFilePath, const int cameraIndex, size_t numFaces)
{
m_camera = std::make_unique<cv::VideoCapture>(cameraIndex);
if (m_camera->isOpened() == false)
{
std::cerr << "Failed opening camera" << std::endl;
exit(-1);
}

m_faceCascade = std::make_unique<cv::CascadeClassifier>(cascadeFilePath);
if (m_camera->isOpened() == false)
{
std::cerr << "Error loading cascade file " << cascadeFilePath << std::endl <<
"Make sure the file exists" << std::endl;
exit(-1);
}

m_originalFrameSize.width = (int)m_camera->get(cv::CAP_PROP_FRAME_WIDTH);
m_originalFrameSize.height = (int)m_camera->get(cv::CAP_PROP_FRAME_HEIGHT);

m_downscaledFrameSize.width = m_downscaledFrameWidth;
m_downscaledFrameSize.height = (m_downscaledFrameSize.width * m_originalFrameSize.height) / m_originalFrameSize.width;

m_ratio.x = (float)m_originalFrameSize.width / m_downscaledFrameSize.width;
m_ratio.y = (float)m_originalFrameSize.height / m_downscaledFrameSize.height;

m_numFaces = numFaces;
}

FaceDetectorAndTracker::~FaceDetectorAndTracker()
{

}

void FaceDetectorAndTracker::operator>>(cv::Mat &frame)
{
if (m_camera->isOpened() == false)
{
frame.release();
return;
}
*m_camera >> frame;

cv::resize(frame, m_downscaledFrame, m_downscaledFrameSize);

if (!m_tracking) // Search for faces on whole frame until 2 faces are found
{
detect();
return;
}
else // if (m_tracking)
{
track();
}
}

std::vector<cv::Rect> FaceDetectorAndTracker::faces()
{
std::vector<cv::Rect> faces;
for (const auto& face : m_facesRects)
{
faces.push_back(cv::Rect(face.x * m_ratio.x, face.y * m_ratio.y, face.width * m_ratio.x, face.height * m_ratio.y));
}
return faces;
}

void FaceDetectorAndTracker::detect()
{
// Minimum face size is 1/5th of screen height
// Maximum face size is 2/3rds of screen height
m_faceCascade->detectMultiScale(m_downscaledFrame, m_facesRects, 1.1, 3, 0,
cv::Size(m_downscaledFrame.rows / 5, m_downscaledFrame.rows / 5),
cv::Size(m_downscaledFrame.rows * 2 / 3, m_downscaledFrame.rows * 2 / 3));

if (m_facesRects.size() < m_numFaces)
{
return;
}
else if (m_facesRects.size() >= m_numFaces)
{
m_facesRects.resize(m_numFaces);
}

// Get face templates
m_faceTemplates.clear();
for (auto face : m_facesRects)
{
face.width /= 2;
face.height /= 2;
face.x += face.width / 2;
face.y += face.height / 2;
m_faceTemplates.push_back(m_downscaledFrame(face).clone());
}

// Get face ROIs
m_faceRois.clear();
for (const auto& face : m_facesRects)
{
m_faceRois.push_back(doubleRectSize(face, m_downscaledFrameSize));
}

// Initialize template matching timers
m_tmRunningInRoi.clear();
m_tmStartTime.clear();
m_tmEndTime.clear();
m_tmRunningInRoi.resize(m_facesRects.size(), false);
m_tmStartTime.resize(m_facesRects.size());
m_tmEndTime.resize(m_facesRects.size());

// Turn on tracking
m_tracking = true;
}

void FaceDetectorAndTracker::track()
{
for (int i = 0; i < m_faceRois.size(); i++)
{
const auto &roi = m_faceRois[i]; // roi

// Detect faces sized +/-20% off biggest face in previous search
const cv::Mat &faceRoi = m_downscaledFrame(roi);
m_faceCascade->detectMultiScale(faceRoi, m_tmpFacesRect, 1.1, 3, 0,
cv::Size(roi.width * 4 / 10, roi.height * 4 / 10),
cv::Size(roi.width * 6 / 10, roi.width * 6 / 10));

if (m_tmpFacesRect.empty()) // No face found in roi... fallback to tm
{
if (m_tmStartTime[i] == 0) // if tm just started start stopwatch
{
m_tmStartTime[i] = cv::getCPUTickCount();
}

// Template matching
cv::matchTemplate(faceRoi, m_faceTemplates[i], m_matchingResult, CV_TM_SQDIFF_NORMED);
cv::normalize(m_matchingResult, m_matchingResult, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
double min, max;
cv::Point minLoc, maxLoc;
cv::minMaxLoc(m_matchingResult, &min, &max, &minLoc, &maxLoc);

// Add roi offset to face position
m_facesRects[i].x = minLoc.x + roi.x - m_faceTemplates[i].cols / 2;
m_facesRects[i].y = minLoc.y + roi.y - m_faceTemplates[i].rows / 2;
m_facesRects[i].width = m_faceTemplates[i].cols * 2;
m_facesRects[i].height = m_faceTemplates[i].rows * 2;


m_tmEndTime[i] = cv::getCPUTickCount();

double duration = (double)(m_tmEndTime[i] - m_tmStartTime[i]) / cv::getTickFrequency();
if (duration > m_tmMaxDuration)
{
m_facesRects.clear();
m_tracking = false;
return; // Stop tracking faces
}
}
else
{
m_tmRunningInRoi[i] = false;
m_tmStartTime[i] = m_tmEndTime[i] = 0;

m_facesRects[i] = m_tmpFacesRect[0];

m_facesRects[i].x += roi.x;
m_facesRects[i].y += roi.y;
}
}

for (int i = 0; i < m_facesRects.size(); i++)
{
for (int j = i + 1; j < m_facesRects.size(); j++)
{
if ((m_facesRects[i] & m_facesRects[j]).area() > 0)
{
m_facesRects.clear();
m_tracking = false;
return;
}
}
}
}

cv::Rect FaceDetectorAndTracker::doubleRectSize(const cv::Rect &inputRect, const cv::Size &frameSize)
{
cv::Rect outputRect;
// Double rect size
outputRect.width = inputRect.width * 2;
outputRect.height = inputRect.height * 2;

// Center rect around original center
outputRect.x = inputRect.x - inputRect.width / 2;
outputRect.y = inputRect.y - inputRect.height / 2;

// Handle edge cases
if (outputRect.x < 0) {
outputRect.width += outputRect.x;
outputRect.x = 0;
}
if (outputRect.y < 0) {
outputRect.height += outputRect.y;
outputRect.y = 0;
}

if (outputRect.x + outputRect.width > frameSize.width) {
outputRect.width = frameSize.width - outputRect.x;
}
if (outputRect.y + outputRect.height > frameSize.height) {
outputRect.height = frameSize.height - outputRect.y;
}

return outputRect;
}
@@ -0,0 +1,97 @@
#pragma once

#include <opencv2/core.hpp>
#include <vector>
#include <string>
#include <memory>

namespace cv
{
class VideoCapture;
class CascadeClassifier;
}



class FaceDetectorAndTracker
{
public:
/*
* Initializes detector with cascade file, initializes camera with camera index and sets number of faces to track
*/
FaceDetectorAndTracker(const std::string cascadeFilePath, const int cameraIndex, size_t numFaces);
~FaceDetectorAndTracker();

/*
* Returns next camera frame and detects faces
*/
void operator>>(cv::Mat &frame);

/*
* Returns vector of detected faces
*/
std::vector<cv::Rect> faces();

private:
void detect();
void track();

/* Returns double inputRect size centered around the same point */
static cv::Rect doubleRectSize(const cv::Rect &rect, const cv::Size &frameSize);

/*
* Private members
*/

/*
* Video capture object used for retrieving camera frames
*/
std::unique_ptr<cv::VideoCapture> m_camera;

/*
* Cascade classifier object used for detecting faces in frames
*/
std::unique_ptr<cv::CascadeClassifier> m_faceCascade;

/*
* Downscaled camera frame. Downscaling speeds up detection
*/
cv::Mat m_downscaledFrame;

/*
* Width of downscaled camera frame. Height is calculated to preserve aspect ratio
*/
static const int m_downscaledFrameWidth = 256;

/*
* Vector of rectangles representing faces in camera frame
*/
std::vector<cv::Rect> m_facesRects;

/*
* Vector of vector of faces. Used in tracking. One vector per detected face
*/
std::vector<cv::Rect> m_tmpFacesRect;

std::vector<bool> m_tmRunningInRoi;
std::vector<long long> m_tmStartTime;
std::vector<long long> m_tmEndTime;

std::vector<cv::Point2f> m_facePositions;
std::vector<cv::Mat> m_faceTemplates;
std::vector<cv::Rect> m_faceRois;

cv::Mat m_matchingResult;

cv::Size m_downscaledFrameSize;
cv::Size m_originalFrameSize;
cv::Point2f m_ratio;

bool m_tracking = false;

size_t m_numFaces = 0;

const double m_tmMaxDuration = 2.0;

};

0 comments on commit 2f7e2d3

Please sign in to comment.
You can’t perform that action at this time.