在这个靠脸吃饭的时代,有张漂亮的脸蛋无疑会令你加分不少;那万一天生的颜值不够怎么办呢?。。。还有美颜来拯救。
现在很多相机以及大多数修图软件都提供了瘦脸的功能。现在主流的瘦脸功能都是基于opengl来完成的。
原图:
瘦脸:
很明显下面的照片要比上面的照片的脸要瘦了很多。 下面一步步去实现这些功能。
VS2019的项目,环境完整,可直接运行
初始化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); }完成