美颜瘦脸
在这个靠脸吃饭的时代,有张漂亮的脸蛋无疑会令你加分不少;那万一天生的颜值不够怎么办呢?。。。还有美颜来拯救。
现在很多相机以及大多数修图软件都提供了瘦脸的功能。现在主流的瘦脸功能都是基于opengl来完成的。
先看效果
原图:
瘦脸:
很明显下面的照片要比上面的照片的脸要瘦了很多。
下面一步步去实现这些功能。
先上源码
实现
识别人脸并定位特征点
用dlib或者face++或者自己训练个神经网络去识别出特征点。
这里我用dlib 识别出特征点
初始化dlib:
dlib::frontal_face_detector detector;
dlib::shape_predictor pose_model;
std::string facemodel = "./model/shape_predictor_68_face_landmarks.dat";
detector = dlib::get_frontal_face_detector();
dlib::deserialize(facemodel) >> pose_model;
识别人脸并保存特征点
dlib::cv_image<dlib::bgr_pixel> cimg(src);
std::vector<dlib::rectangle> faces = detector(cimg); // Detect faces
dlib::full_object_detection shape; // Find the pose of one face
facelandmarks.clear();
if (faces.size() > 0) {
shape = pose_model(cimg, faces[0]);
facerect = cv::Rect(cv::Point2i(faces[0].left(), faces[0].top()), cv::Point2i(faces[0].right() + 1, faces[0].bottom() + 1));
for (int i = 0; i < 68; i++) {
facelandmarks.push_back(cv::Point2f(shape.part(i).x(), shape.part(i).y()));
}
}
else {
return -1;
}
识别的特征点如下 共68个:
人脸分割
这里为了简单只将图片分为26个三角形。若想效果更平滑,可以将图片分的更细。
//人脸分割
cv::Point2f LALL0 = cv::Point2f(-1.0f, -1.0f);
cv::Point2f LALL1 = cv::Point2f(-1.0f, 1.0f);
cv::Point2f LALL2 = cv::Point2f(1.0f, 1.0f);
cv::Point2f LALL3 = cv::Point2f(1.0f, -1.0f);
cv::Point2f LFace30 = cv::Point2f(facelandmarks[30].x * 2.0 / width - 1.0, (height - facelandmarks[30].y) * 2.0 / height - 1.0);
cv::Point2f LFace3 = cv::Point2f(facelandmarks[3].x * 2.0 / width - 1.0, (height - facelandmarks[3].y) * 2.0 / height - 1.0);
cv::Point2f LFace4 = cv::Point2f(facelandmarks[4].x * 2.0 / width - 1.0, (height - facelandmarks[4].y) * 2.0 / height - 1.0);
cv::Point2f LFace5 = cv::Point2f(facelandmarks[5].x * 2.0 / width - 1.0, (height - facelandmarks[5].y) * 2.0 / height - 1.0);
cv::Point2f LFace6 = cv::Point2f(facelandmarks[6].x * 2.0 / width - 1.0, (height - facelandmarks[6].y) * 2.0 / height - 1.0);
cv::Point2f LFace7 = cv::Point2f(facelandmarks[7].x * 2.0 / width - 1.0, (height - facelandmarks[7].y) * 2.0 / height - 1.0);
cv::Point2f LFace8 = cv::Point2f(facelandmarks[8].x * 2.0 / width - 1.0, (height - facelandmarks[8].y) * 2.0 / height - 1.0);
cv::Point2f LFace9 = cv::Point2f(facelandmarks[9].x * 2.0 / width - 1.0, (height - facelandmarks[9].y) * 2.0 / height - 1.0);
cv::Point2f LFace10 = cv::Point2f(facelandmarks[10].x * 2.0 / width - 1.0, (height - facelandmarks[10].y) * 2.0 / height - 1.0);
cv::Point2f LFace11 = cv::Point2f(facelandmarks[11].x * 2.0 / width - 1.0, (height - facelandmarks[11].y) * 2.0 / height - 1.0);
cv::Point2f LFace12 = cv::Point2f(facelandmarks[12].x * 2.0 / width - 1.0, (height - facelandmarks[12].y) * 2.0 / height - 1.0);
cv::Point2f LFace13 = cv::Point2f(facelandmarks[13].x * 2.0 / width - 1.0, (height - facelandmarks[13].y) * 2.0 / height - 1.0);
cv::Point2f VALL0 = cv::Point2f(0.0f, 1.0f);
cv::Point2f VALL1 = cv::Point2f(0.0f, 0.0f);
cv::Point2f VALL2 = cv::Point2f(1.0f, 0.0f);
cv::Point2f VALL3 = cv::Point2f(1.0f, 1.0f);
cv::Point2f VFace30 = cv::Point2f(facelandmarks[30].x / width, facelandmarks[30].y / height);
cv::Point2f VFace3 = cv::Point2f(facelandmarks[3].x / width, facelandmarks[3].y / height);
cv::Point2f VFace4 = cv::Point2f(facelandmarks[4].x / width, facelandmarks[4].y / height);
cv::Point2f VFace5 = cv::Point2f(facelandmarks[5].x / width, facelandmarks[5].y / height);
cv::Point2f VFace6 = cv::Point2f(facelandmarks[6].x / width, facelandmarks[6].y / height);
cv::Point2f VFace7 = cv::Point2f(facelandmarks[7].x / width, facelandmarks[7].y / height);
cv::Point2f VFace8 = cv::Point2f(facelandmarks[8].x / width, facelandmarks[8].y / height);
cv::Point2f VFace9 = cv::Point2f(facelandmarks[9].x / width, facelandmarks[9].y / height);
cv::Point2f VFace10 = cv::Point2f(facelandmarks[10].x / width, facelandmarks[10].y / height);
cv::Point2f VFace11 = cv::Point2f(facelandmarks[11].x / width, facelandmarks[11].y / height);
cv::Point2f VFace12 = cv::Point2f(facelandmarks[12].x / width, facelandmarks[12].y / height);
cv::Point2f VFace13 = cv::Point2f(facelandmarks[13].x / width, facelandmarks[13].y / height);
float vertices[] = {
//----位置---- ---纹理---
LALL0.x, LALL0.y, 0.0f, VALL0.x, VALL0.y,
LALL1.x, LALL1.y, 0.0f, VALL1.x, VALL1.y,
LALL2.x, LALL2.y, 0.0f, VALL2.x, VALL2.y,
LALL3.x, LALL3.y, 0.0f, VALL3.x, VALL3.y,
LFace30.x, LFace30.y, 0.0f, VFace30.x, VFace30.y,
LFace3.x, LFace3.y, 0.0f, VFace3.x, VFace3.y,
LFace4.x, LFace4.y, 0.0f, VFace4.x, VFace4.y,
LFace5.x, LFace5.y, 0.0f, VFace5.x, VFace5.y,
LFace6.x, LFace6.y, 0.0f, VFace6.x, VFace6.y,
LFace7.x, LFace7.y, 0.0f, VFace7.x, VFace7.y,
LFace8.x, LFace8.y, 0.0f, VFace8.x, VFace8.y,
LFace9.x, LFace9.y, 0.0f, VFace9.x, VFace9.y,
LFace10.x, LFace10.y, 0.0f, VFace10.x, VFace10.y,
LFace11.x, LFace11.y, 0.0f, VFace11.x, VFace11.y,
LFace12.x, LFace12.y, 0.0f, VFace12.x, VFace12.y,
LFace13.x, LFace13.y, 0.0f, VFace13.x, VFace13.y
};
unsigned int indices[] = {
0,1,5,
5,1,4,
4,1,2,
2,4,15,
15,2,3,
0,5,6,
0,6,7,
0,7,8,
0,8,9,
0,9,10,
0,10,3,
3,10,11,
3,11,12,
3,12,13,
3,13,14,
3,14,15,
4,5,6,
4,6,7,
4,7,8,
4,8,9,
4,9,10,
4,10,11,
4,11,12,
4,12,13,
4,13,14,
4,14,15
};
瘦脸变形
只需要将图中标记的点按照箭头方向拖动,即可实现瘦脸,为了不显得太过于尖锐,我们将范围包括到附近的几个点。
shader:
#version 330 core
precision mediump float;
in vec2 TexCoord;
out vec4 outColor;
uniform sampler2D inputTexture;
uniform float face5x;
uniform float face5y;
vec2 stretchFun(vec2 textureCoord, vec2 originPosition, vec2 targetPosition, float radius,float curve)
{
vec2 direction = targetPosition - originPosition;
float infect = distance(textureCoord, originPosition)/radius;
infect =1.0 - pow(infect,curve);
infect = clamp(infect,0.0,1.0);
vec2 offset = direction * infect;
vec2 result = textureCoord - offset;
return result;
}
void main(){
vec2 A1 = vec2(face5x,face5y);
vec2 A2 = vec2(face5x+0.02f,face5y + 0.01f);
vec2 TexCoord2 = stretchFun(TexCoord,A1,A2,0.19f,2.0f);
vec3 tmpColor = texture(inputTexture, TexCoord2).rgb;
outColor = vec4(tmpColor,1.0f);
}
像素偏移后结果
去掉特征点后结果
完成