基于人脸68特征点识别的美颜算法(一) 大眼算法 C++

1、加载一张原图,并识别人脸的68个特征点

cpp 复制代码
	cv::Mat img = cv::imread("5.jpg");
    // 人脸68特征点的识别函数
	vector<Point2f> points_vec = dectectFace68(img);
    // 大眼效果函数
	Mat dst0 = on_BigEye(800, img, points_vec);

2、函数

cpp 复制代码
vector<Point2f> dectectFace68(Mat src)
{
	vector<Point2f> points_vec;
	
	int* pResults = NULL;
	//在检测函数中使用了pBuffer。  
	//如果你调用多个线程中的函数,请为每个线程创建一个缓冲区!  
	unsigned char* pBuffer = (unsigned char*)malloc(DETECT_BUFFER_SIZE);
	if (!pBuffer)
	{
		fprintf(stderr, "Can not alloc buffer.\n");
		//return 100;
	}
	Mat gray;
	cvtColor(src, gray, CV_BGR2GRAY);
	int doLandmark = 1;// do landmark detection  
	pResults = facedetect_multiview_reinforce(pBuffer, (unsigned char*)(gray.ptr(0)), gray.cols, gray.rows, (int)gray.step,
		1.2f, 2, 48, 0, doLandmark);

	int cxa = *pResults;

	ofstream file("facedata.txt", ios::out);
	//打印检测结果 



	if (0 == cxa)
	{
	}
	else
	{
		
		for (int i = 0; i < (pResults ? *pResults : 0); i++)
		{
			
			short* p = ((short*)(pResults + 1)) + 142 * i;
			//rectangle(src, Rect(p[0], p[1], p[2], p[3]), Scalar(0, 255, 0), 2);
			if (doLandmark)
			{
				for (int j = 0; j < 68; j++)
				{
					char c[8];
					_itoa(j, c, 10);
					
					

					Point2f ff(p[6 + 2 * j], p[6 + 2 * j + 1]);
					points_vec.push_back(ff);
					file << ff.x << "\t" << ff.y << endl;

				/*	circle(src, Point((int)p[6 + 2 * j], (int)p[6 + 2 * j + 1]), 3, Scalar(0, 0, 255), 3);
					CvPoint font;
					putText(src, c, Point((int)p[6 + 2 * j], (int)p[6 + 2 * j + 1]), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 23, 0), 1);*/
					
				}
			}
		}


	

	}



	return points_vec;
}




// 双线性插值算法
void BilinearInsert(Mat& src, Mat& dst, float ux, float uy, int i, int j)
{
	auto Abs = [&](float f) {
		return f > 0 ? f : -f;
		};

	int c = src.channels();
	if (c == 3)
	{
		//存储图像得浮点坐标
		CvPoint2D32f uv;
		CvPoint3D32f f1;
		CvPoint3D32f f2;

		//取整数
		int iu = (int)ux;
		int iv = (int)uy;
		uv.x = iu + 1;
		uv.y = iv + 1;

		//step图象像素行的实际宽度  三个通道进行计算(0 , 1 2  三通道)
		f1.x = ((uchar*)(src.data + src.step * iv))[iu * 3] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * iv))[(iu + 1) * 3] * (uv.x - iu);
		f1.y = ((uchar*)(src.data + src.step * iv))[iu * 3 + 1] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * iv))[(iu + 1) * 3 + 1] * (uv.x - iu);
		f1.z = ((uchar*)(src.data + src.step * iv))[iu * 3 + 2] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * iv))[(iu + 1) * 3 + 2] * (uv.x - iu);


		f2.x = ((uchar*)(src.data + src.step * (iv + 1)))[iu * 3] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * (iv + 1)))[(iu + 1) * 3] * (uv.x - iu);
		f2.y = ((uchar*)(src.data + src.step * (iv + 1)))[iu * 3 + 1] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * (iv + 1)))[(iu + 1) * 3 + 1] * (uv.x - iu);
		f2.z = ((uchar*)(src.data + src.step * (iv + 1)))[iu * 3 + 2] * (1 - Abs(uv.x - iu)) + \
			((uchar*)(src.data + src.step * (iv + 1)))[(iu + 1) * 3 + 2] * (uv.x - iu);

		((uchar*)(dst.data + dst.step * j))[i * 3] = f1.x * (1 - Abs(uv.y - iv)) + f2.x * (Abs(uv.y - iv));  //三个通道进行赋值
		((uchar*)(dst.data + dst.step * j))[i * 3 + 1] = f1.y * (1 - Abs(uv.y - iv)) + f2.y * (Abs(uv.y - iv));
		((uchar*)(dst.data + dst.step * j))[i * 3 + 2] = f1.z * (1 - Abs(uv.y - iv)) + f2.z * (Abs(uv.y - iv));

	}
}


//图像局部缩放算法
void LocalTranslationWarp_Eye(Mat& img, Mat& dst, int warpX, int warpY, int endX, int endY, float radius)
{
	//平移距离 
	float ddradius = radius * radius;
	//计算|m-c|^2
	//size_t mc = (endX - warpX) * (endX - warpX) + (endY - warpY) * (endY - warpY);
	//计算 图像的高  宽 通道数量
	int height = img.rows;
	int width = img.cols;
	int chan = img.channels();

	auto Abs = [&](float f) 
		{
		return f > 0 ? f : -f;
		};

	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < height; j++)
		{
			// # 计算该点是否在形变圆的范围之内
			//# 优化,第一步,直接判断是会在(startX, startY)的矩阵框中
			if ((Abs(i - warpX) > radius) && (Abs(j - warpY) > radius))
				continue;

			float distance = (i - warpX) * (i - warpX) + (j - warpY) * (j - warpY);
			if (distance < ddradius)
			{
				float rnorm = sqrt(distance) / radius;
				float ratio = 1 - (rnorm - 1) * (rnorm - 1) * 0.5;
				//映射原位置
				float UX = warpX + ratio * (i - warpX);
				float UY = warpY + ratio * (j - warpY);

				//根据双线性插值得到UX UY的值
				BilinearInsert(img, dst, UX, UY, i, j);
			}
		}
	}
}

//大眼效果
Mat on_BigEye(int b, Mat src, vector<Point2f> points_vec)
{
	
	Mat dst = src.clone();


	Point2f left_landmark = points_vec[38];
	Point2f	left_landmark_down = points_vec[27];

	Point2f	right_landmark = points_vec[44];
	Point2f	right_landmark_down = points_vec[27];

	Point2f	endPt = points_vec[30];

	//# 计算第4个点到第6个点的距离作为距离
	/*float r_left = sqrt(
		(left_landmark.x - left_landmark_down.x) * (left_landmark.x - left_landmark_down.x) +
		(left_landmark.y - left_landmark_down.y) * (left_landmark.y - left_landmark_down.y));
	cout << "左眼距离:" << r_left;*/
	float r_left = b;

	//	# 计算第14个点到第16个点的距离作为距离
	//float	r_right = sqrt(
	//	(right_landmark.x - right_landmark_down.x) * (right_landmark.x - right_landmark_down.x) +
	//	(right_landmark.y - right_landmark_down.y) * (right_landmark.y - right_landmark_down.y));
	//cout << "右眼距离:" << r_right;
	float r_right = b;
	//	# 瘦左                     
	//LocalTranslationWarp_Eye(src, dst, left_landmark.x, left_landmark.y, endPt.x, endPt.y, r_left);
	//	# 瘦右
	LocalTranslationWarp_Eye(src, dst, right_landmark.x, right_landmark.y, endPt.x, endPt.y, r_right);


	return dst;
}

3、图像结果

大眼睛结果

相关推荐
IT数据小能手12 分钟前
使用Go语言实现高效的数据挖掘
开发语言·golang·数据挖掘
bananawolf13 分钟前
C++部分复习笔记上
c++·笔记
ztenv21 分钟前
C++23特性一览
linux·c++·c++23
Ai实战22 分钟前
上帝之眼(BEVSee):多相机间无需标定,将各自目标统一到同一坐标系下(代码开源,提供数据集)
算法
xy1899027 分钟前
C++线程安全是如何保证的?线程不安全是如何出现的?有什么处理方案呢
开发语言·c++·安全
ztenv27 分钟前
编译器对C++23的支持程度
c++·c++23
方程式sunny30 分钟前
C#的五大设计原则-solid原则
开发语言·c#
灭霸112332 分钟前
力扣 用队列实现栈(Java)
算法·leetcode·职场和发展
IT数据小能手36 分钟前
PHP多线程爬虫:高效解析电商网页内容
开发语言·爬虫·php
范范082538 分钟前
Symfony实战手册:PHP框架的高级应用技巧
开发语言·php·symfony