Skip to content

Instantly share code, notes, and snippets.

@gu-ma
Last active January 16, 2018 22:20
Show Gist options
  • Save gu-ma/88485ca9efab1c7cdcf75247ac8fbd81 to your computer and use it in GitHub Desktop.
Save gu-ma/88485ca9efab1c7cdcf75247ac8fbd81 to your computer and use it in GitHub Desktop.
Face alignment with openFramework + ofxFaceTracker2
//
// 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 */
@gu-ma
Copy link
Author

gu-ma commented Sep 15, 2017

Also requires ofxOpenCv & ofxCV

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment