人脸检测之后的旋转摆正与裁剪

使用检测网络检测出人脸之后,在下一步比对或者训练之前,要对人脸进行对齐,我觉得对齐这个词不够精确,我称之为摆正。通过调研,我使用的方法是使用两只眼睛的坐标作为摆正的标准(当然,这需要检测网路能够输出眼睛坐标),摆正之后要对人脸进行重新裁剪,这需要利用眼睛、鼻子和嘴角的信息。归纳起来就是:

1、得出两只眼睛形成的直线的夹角,按照该夹角的反方向进行图像的旋转;旋转之后关键的一步是根据旋转公式,获得旋转之后的特征点的坐标

2、摆正之后,根据眼睛、鼻子、嘴巴纵向距离,估测要裁剪的人脸的高度;

3、确定高度之后,设定高宽比(如1:1,或者1.16:1)等等,确定要裁出的人脸的宽度;

4、根据鼻子和两只眼睛横向的距离比,计算左右两边的具体坐标


                std::vector<cv::Point> points;//points(vector)元素是人脸两只眼睛所在的点,应该是为了拟合直线以进行人脸旋转
		
                cv::Point eye0 = cv::Point(res[max_index].points_x[0], res[max_index].points_y[0]);
                cv::Point eye1 = cv::Point(res[max_index].points_x[1], res[max_index].points_y[1]);
                cv::Point mouth_0 = cv::Point(res[max_index].points_x[3], res[max_index].points_y[3]);
                cv::Point mouth_1 = cv::Point(res[max_index].points_x[4], res[max_index].points_y[4]);
                cv::Point nose = cv::Point(res[max_index].points_x[2], res[max_index].points_y[2]);

                cv::line(image, eye0, eye1, (255, 0, 255), 3);
                cv::line(image, mouth_0, mouth_1, (0, 0, 255), 3);
                cv::imshow("1", image);

                points.push_back(eye0);
                points.push_back(eye1);
		cv::Vec4f line;//vec4f类型的line是拟合的结果,前两个元素表示方向,后两个元素表示直线上一点。
		cv::fitLine(points,
        			line,
			CV_DIST_HUBER,
			0,
			0.01,
			0.01);

		double cos_theta = line[0];
		double sin_theta = line[1];
		double x0 = line[2], y0 = line[3];

		double phi = atan2(sin_theta, cos_theta) + 3.1416 / 2.0;
		double rho = y0 * cos_theta - x0 * sin_theta;

		/*std::cout << "phi = " << phi / 3.1416 * 180 << std::endl;
		std::cout << "rho = " << rho << std::endl;*/
		//DrawLine(image, phi, rho, cv::Scalar(255));
		double k = sin_theta / cos_theta;
		double b = y0 - k * x0;
		double x = 0;
		double y = k * x + b;
		/*std::cout << k << std::endl;
		std::cout << b << std::endl;*/

		cv::Mat input_img = image.clone();
		cv::Mat temp_img;
		// 将图像按照相应的角度旋转
		float angle = phi / 3.1416 * 180 - 90;          //角度
		float radian = (float)(angle / 180.0 * CV_PI);  //弧度

		// 填充图像使其符合旋转要求
		int uniSize = (int)(max(input_img.cols, input_img.rows)* 1.414);  //以宽、高较大者的根号2倍,作为啥呢?
		int dx = (int)(uniSize - input_img.cols) / 2;//原来是作为扩充宽度
		int dy = (int)(uniSize - input_img.rows) / 2;
		copyMakeBorder(input_img, temp_img, dy, dy, dx, dx, cv::BORDER_CONSTANT);//以周边恒定值0为扩充值进行扩充
		//copyMakeBorder(temp_mat, temp_mat, dy, dy, dx, dx, cv::BORDER_CONSTANT);
		// 旋转中心
		//cv::Point2f center((float)(temp_img.cols / 2), (float)(temp_img.rows / 2));//看旋转中心的值就知道是以图片中作为旋转中心的
		cv::Point2f center((float)(image.cols / 2), (float)(image.rows / 2));
		cv::Mat affine_matrix = getRotationMatrix2D(center, angle, 1.0);
		
		
		
		// 旋转
		warpAffine(temp_img, temp_img, affine_matrix, temp_img.size());
		//warpAffine(temp_mat, temp_mat, affine_matrix, temp_img.size());
		warpAffine(temp_mat, temp_mat, affine_matrix, image.size());

		radian = -radian;

		/*float eye0_new_x = (eye0.x - center.x)*cos(radian) - (eye0.y - center.y)*sin(radian) + center.x;  //以center为坐标原点
		float eye0_new_y = (eye0.x - center.x)*sin(radian) + (eye0.y - center.y)*cos(radian) + center.y;

		float eye1_new_x = (eye1.x - center.x)*cos(radian) - (eye1.y - center.y)*sin(radian) + center.x;
		float eye1_new_y = (eye1.x - center.x)*sin(radian) + (eye1.y - center.y)*cos(radian) + center.y;

		cv::Point2f eye0_new(eye0_new_x, eye0_new_y);
		cv::Point2f eye1_new(eye1_new_x, eye1_new_y);*/
		
		cv::Point2f eye0_new = get_new_point(eye0, center, radian);
		cv::Point2f eye1_new = get_new_point(eye1, center, radian);
		cv::Point2f mouth0_new = get_new_point(mouth_0, center, radian);
		cv::Point2f mouth1_new = get_new_point(mouth_1, center, radian);
		cv::Point2f nose_new = get_new_point(nose, center, radian);

		cout << eye0_new.x << "  "<< eye1_new.x << "  " << nose_new.x << endl;

		//cv::line(temp_mat, eye0_new, eye1_new, (255, 255, 255), 2);
		//cv::line(temp_mat, eye0_new, nose_new, (0, 0, 255), 2);
		//cv::line(temp_mat, mouth0_new, mouth1_new, (255, 255, 255), 5);

		cv::imshow("test", temp_mat);
		
		float delta_W = eye1_new.x - eye0_new.x;
		float delta_H = (mouth0_new.y + mouth1_new.y) / 2 - eye0_new.y;  //眼睛和嘴唇之间高度差
		
		cout << "两眼间距:" << delta_W << endl;
		cout << "眼睛、嘴角高度差:" << delta_H << endl;

		//确定要截出的顶端和底端
		float y_top = eye0_new.y - delta_H / 2.14;
		float y_bottom = (mouth0_new.y + mouth1_new.y) / 2 + delta_H / 3.33;
		cout << y_bottom - y_top << endl;

		float width = (float)(y_bottom - y_top);

		float both_sides = width - delta_W;
		float h_distance_eye0_nose = abs(nose.x - eye0_new.x);
		float h_distance_eye1_nose = abs(eye1_new.x - nose.x);
		float h_ratio_0 = h_distance_eye0_nose /( h_distance_eye0_nose + h_distance_eye1_nose);

		float x_left = 0.0;
		float x_right = 0.0;

		if (h_ratio_0 >= 0.5)
		{
			 x_left = min(eye0_new.x, nose.x) - both_sides*h_ratio_0;
			 x_right = width + x_left;
		}
		else
		{
			x_right = max(nose.x, eye1_new.x) + both_sides*(1-h_ratio_0);
			x_left = x_right - width;
		}

		rect = cv::Rect(cv::Point(x_left, y_top), cv::Point(x_right, y_bottom));

		if (rect.x < 0) { rect.x = 0; }
		if (rect.y < 0) { rect.y = 0; }
		if (rect.x > temp_mat.cols){ rect.x = temp_mat.cols; }
		if (rect.y > temp_mat.rows){ rect.y = temp_mat.rows; }
		if (rect.x + rect.width > temp_mat.cols){ rect.width = temp_mat.cols - rect.x; }
		if (rect.y + rect.height > temp_mat.rows){ rect.height = temp_mat.rows - rect.y; }

		cv::Mat face_cut(temp_mat, rect);

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值