美顏瘦臉
在這個靠臉喫飯的時代,有張漂亮的臉蛋無疑會令你加分不少;那萬一天生的顏值不夠怎麼辦呢?。。。還有美顏來拯救。
現在很多相機以及大多數修圖軟件都提供了瘦臉的功能。現在主流的瘦臉功能都是基於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);
}
像素偏移後結果
去掉特徵點後結果
完成