Last active
January 16, 2018 22:20
-
-
Save gu-ma/88485ca9efab1c7cdcf75247ac8fbd81 to your computer and use it in GitHub Desktop.
Face alignment with openFramework + ofxFaceTracker2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// faceUtils.h | |
// Facetracker_face_alignment | |
// | |
// Created by Guillaume on 21.08.17. | |
// | |
// http://www.pyimagesearch.com/2017/05/22/face-alignment-with-opencv-and-python/ | |
// https://github.com/MasteringOpenCV/code/blob/master/Chapter8_FaceRecognition/preprocessFace.cpp | |
// | |
// | |
#ifndef faceUtils_h | |
#define faceUtils_h | |
class faceUtils { | |
public: | |
//-------------------------------------------------------------- | |
ofImage alignFace(ofImage &srcImg, ofxFaceTracker2Instance &instance, int desiredFaceWidth, | |
float desiredLeftEyeX = 0.4, float desiredLeftEyeY = 0.4, float scale = 2, bool rotate = true) { | |
// square box | |
int desiredFaceHeight = desiredFaceWidth; | |
// get face bounding box | |
ofRectangle rect = instance.getBoundingBox(); | |
rect.setHeight(rect.getWidth()); | |
// crop a bigger area | |
ofRectangle scaledRect = rect; | |
// Make sure the rectangle won't be bigger than the img | |
float scaleX = ofClamp(scale, 0, srcImg.getWidth()/rect.getWidth()); | |
float scaleY = ofClamp(scale, 0, srcImg.getHeight()/rect.getHeight()); | |
scale = min(scaleX, scaleY); | |
// Scale rectangle | |
scaledRect.scaleFromCenter(scale); | |
// Make sure the rectangle stays within the img boundaries | |
int x = ofClamp(scaledRect.x, 0, srcImg.getWidth()-scaledRect.width); | |
int y = ofClamp(scaledRect.y, 0, srcImg.getHeight()-scaledRect.height); | |
// Crop image | |
cv::Mat mat = ofxCv::toCv(srcImg); | |
cv::Mat croppedMat = mat(cv::Rect(x, y, scaledRect.width, scaledRect.height)); | |
// detect eyes | |
ofPolyline leftEye = instance.getLandmarks().getImageFeature(ofxFaceTracker2Landmarks::LEFT_EYE); | |
ofPolyline rightEye = instance.getLandmarks().getImageFeature(ofxFaceTracker2Landmarks::RIGHT_EYE); | |
// Check if both eyes were detected. | |
if (leftEye.size()!=0 && rightEye.size()!=0) { | |
// Get eyes center | |
ofVec3f leftEyeCenter = leftEye.getCentroid2D(); | |
ofVec3f rightEyeCenter = rightEye.getCentroid2D(); | |
// Calculate coordinate of the eyes within the face bounding box | |
leftEyeCenter = leftEyeCenter - scaledRect.getTopLeft(); | |
rightEyeCenter = rightEyeCenter - scaledRect.getTopLeft(); | |
// Get the angle between the 2 eyes. | |
float dY = rightEyeCenter.y - leftEyeCenter.y; | |
float dX = rightEyeCenter.x - leftEyeCenter.x; | |
double len = sqrt(dX*dX + dY*dY); | |
// rotate if necessary | |
float angle; | |
if (rotate) angle = ofRadToDeg(atan2(dY, dX)); | |
else angle = 0; | |
// Get the center between the 2 eyes. | |
cv::Point2f eyesCenter = cv::Point2f( (leftEyeCenter.x + rightEyeCenter.x) * 0.5f, (leftEyeCenter.y + rightEyeCenter.y) * 0.5f ); | |
// ofVec3f eyesCenter = ofVec3f( (leftEyeCenter.x + rightEyeCenter.x) * 0.5f, (leftEyeCenter.y + rightEyeCenter.y) * 0.5f ); | |
// Hand measurements shown that the left eye center should ideally be at roughly (0.19, 0.14) of a scaled face image. | |
const double DESIRED_RIGHT_EYE_X = (1.0f - desiredLeftEyeX); | |
// Get the amount we need to scale the image to be the desired fixed size we want. | |
double desiredLen = (DESIRED_RIGHT_EYE_X - desiredLeftEyeX) * desiredFaceWidth; | |
double scale = desiredLen / len; | |
// Get the transformation matrix for rotating and scaling the face to the desired angle & size. | |
cv::Mat rot_mat = cv::getRotationMatrix2D(eyesCenter, angle, scale); | |
// Shift the center of the eyes to be the desired center between the eyes. | |
rot_mat.at<double>(0, 2) += desiredFaceWidth * 0.5f - eyesCenter.x; | |
rot_mat.at<double>(1, 2) += desiredFaceHeight * desiredLeftEyeY - eyesCenter.y; | |
// Rotate and scale and translate the image to the desired angle & size & position! | |
// Note that we use 'w' for the height instead of 'h', because the input face has 1:1 aspect ratio. | |
cv::Mat warped = cv::Mat(desiredFaceHeight, desiredFaceWidth, CV_8U, cvScalar(128)); // Clear the output image to a default grey. | |
warpAffine(croppedMat, warped, rot_mat, warped.size()); | |
croppedMat = warped; | |
} | |
ofImage img; | |
ofPixels px; | |
ofxCv::copy(croppedMat, px); | |
img.setFromPixels(px); | |
img.resize(desiredFaceWidth, desiredFaceHeight); | |
return img; | |
} | |
}; | |
#endif /* faceUtils_h */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also requires ofxOpenCv & ofxCV