opengl 實現瘦臉等臉部微調

美顏瘦臉

在這個靠臉喫飯的時代,有張漂亮的臉蛋無疑會令你加分不少;那萬一天生的顏值不夠怎麼辦呢?。。。還有美顏來拯救。

現在很多相機以及大多數修圖軟件都提供了瘦臉的功能。現在主流的瘦臉功能都是基於opengl來完成的。

先看效果

原圖:
在這裏插入圖片描述

瘦臉:
在這裏插入圖片描述

很明顯下面的照片要比上面的照片的臉要瘦了很多。
下面一步步去實現這些功能。

先上源碼

VS2019的項目,環境完整,可直接運行

實現

識別人臉並定位特徵點

用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);
}

像素偏移後結果

在這裏插入圖片描述

去掉特徵點後結果

在這裏插入圖片描述

完成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章