人脸裁剪

 

 

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

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

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

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

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

 

 
  1.                 std::vector points;//points(vector)元素是人脸两只眼睛所在的点,应该是为了拟合直线以进行人脸旋转

  2.  
  3.                 cv::Point eye0 = cv::Point(res[max_index].points_x[0], res[max_index].points_y[0]);

  4.                 cv::Point eye1 = cv::Point(res[max_index].points_x[1], res[max_index].points_y[1]);

  5.                 cv::Point mouth_0 = cv::Point(res[max_index].points_x[3], res[max_index].points_y[3]);

  6.                 cv::Point mouth_1 = cv::Point(res[max_index].points_x[4], res[max_index].points_y[4]);

  7.                 cv::Point nose = cv::Point(res[max_index].points_x[2], res[max_index].points_y[2]);

  8.  
  9.                 cv::line(image, eye0, eye1, (255, 0, 255), 3);

  10.                 cv::line(image, mouth_0, mouth_1, (0, 0, 255), 3);

  11.                 cv::imshow("1", image);

  12.  
  13.                 points.push_back(eye0);

  14.                 points.push_back(eye1);

  15. cv::Vec4f line;//vec4f类型的line是拟合的结果,前两个元素表示方向,后两个元素表示直线上一点。

  16. cv::fitLine(points,

  17.          line,

  18. CV_DIST_HUBER,

  19. 0,

  20. 0.01,

  21. 0.01);

  22.  
  23. double cos_theta = line[0];

  24. double sin_theta = line[1];

  25. double x0 = line[2], y0 = line[3];

  26.  
  27. double phi = atan2(sin_theta, cos_theta) + 3.1416 / 2.0;

  28. double rho = y0 * cos_theta - x0 * sin_theta;

  29.  
  30. /*std::cout << "phi = " << phi / 3.1416 * 180 << std::endl;

  31. std::cout << "rho = " << rho << std::endl;*/

  32. //DrawLine(image, phi, rho, cv::Scalar(255));

  33. double k = sin_theta / cos_theta;

  34. double b = y0 - k * x0;

  35. double x = 0;

  36. double y = k * x + b;

  37. /*std::cout << k << std::endl;

  38. std::cout << b << std::endl;*/

  39.  
  40. cv::Mat input_img = image.clone();

  41. cv::Mat temp_img;

  42. // 将图像按照相应的角度旋转

  43. float angle = phi / 3.1416 * 180 - 90; //角度

  44. float radian = (float)(angle / 180.0 * CV_PI); //弧度

  45.  
  46. // 填充图像使其符合旋转要求

  47. int uniSize = (int)(max(input_img.cols, input_img.rows)* 1.414); //以宽、高较大者的根号2倍,作为啥呢?

  48. int dx = (int)(uniSize - input_img.cols) / 2;//原来是作为扩充宽度

  49. int dy = (int)(uniSize - input_img.rows) / 2;

  50. copyMakeBorder(input_img, temp_img, dy, dy, dx, dx, cv::BORDER_CONSTANT);//以周边恒定值0为扩充值进行扩充

  51. //copyMakeBorder(temp_mat, temp_mat, dy, dy, dx, dx, cv::BORDER_CONSTANT);

  52. // 旋转中心

  53. //cv::Point2f center((float)(temp_img.cols / 2), (float)(temp_img.rows / 2));//看旋转中心的值就知道是以图片中作为旋转中心的

  54. cv::Point2f center((float)(image.cols / 2), (float)(image.rows / 2));

  55. cv::Mat affine_matrix = getRotationMatrix2D(center, angle, 1.0);

  56.  
  57.  
  58.  
  59. // 旋转

  60. warpAffine(temp_img, temp_img, affine_matrix, temp_img.size());

  61. //warpAffine(temp_mat, temp_mat, affine_matrix, temp_img.size());

  62. warpAffine(temp_mat, temp_mat, affine_matrix, image.size());

  63.  
  64. radian = -radian;

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

  67. float eye0_new_y = (eye0.x - center.x)*sin(radian) + (eye0.y - center.y)*cos(radian) + center.y;

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

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

  71.  
  72. cv::Point2f eye0_new(eye0_new_x, eye0_new_y);

  73. cv::Point2f eye1_new(eye1_new_x, eye1_new_y);*/

  74.  
  75. cv::Point2f eye0_new = get_new_point(eye0, center, radian);

  76. cv::Point2f eye1_new = get_new_point(eye1, center, radian);

  77. cv::Point2f mouth0_new = get_new_point(mouth_0, center, radian);

  78. cv::Point2f mouth1_new = get_new_point(mouth_1, center, radian);

  79. cv::Point2f nose_new = get_new_point(nose, center, radian);

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

  82.  
  83. //cv::line(temp_mat, eye0_new, eye1_new, (255, 255, 255), 2);

  84. //cv::line(temp_mat, eye0_new, nose_new, (0, 0, 255), 2);

  85. //cv::line(temp_mat, mouth0_new, mouth1_new, (255, 255, 255), 5);

  86.  
  87. cv::imshow("test", temp_mat);

  88.  
  89. float delta_W = eye1_new.x - eye0_new.x;

  90. float delta_H = (mouth0_new.y + mouth1_new.y) / 2 - eye0_new.y; //眼睛和嘴唇之间高度差

  91.  
  92. cout << "两眼间距:" << delta_W << endl;

  93. cout << "眼睛、嘴角高度差:" << delta_H << endl;

  94.  
  95. //确定要截出的顶端和底端

  96. float y_top = eye0_new.y - delta_H / 2.14;

  97. float y_bottom = (mouth0_new.y + mouth1_new.y) / 2 + delta_H / 3.33;

  98. cout << y_bottom - y_top << endl;

  99.  
  100. float width = (float)(y_bottom - y_top);

  101.  
  102. float both_sides = width - delta_W;

  103. float h_distance_eye0_nose = abs(nose.x - eye0_new.x);

  104. float h_distance_eye1_nose = abs(eye1_new.x - nose.x);

  105. float h_ratio_0 = h_distance_eye0_nose /( h_distance_eye0_nose + h_distance_eye1_nose);

  106.  
  107. float x_left = 0.0;

  108. float x_right = 0.0;

  109.  
  110. if (h_ratio_0 >= 0.5)

  111. {

  112. x_left = min(eye0_new.x, nose.x) - both_sides*h_ratio_0;

  113. x_right = width + x_left;

  114. }

  115. else

  116. {

  117. x_right = max(nose.x, eye1_new.x) + both_sides*(1-h_ratio_0);

  118. x_left = x_right - width;

  119. }

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

  122.  
  123. if (rect.x < 0) { rect.x = 0; }

  124. if (rect.y < 0) { rect.y = 0; }

  125. if (rect.x > temp_mat.cols){ rect.x = temp_mat.cols; }

  126. if (rect.y > temp_mat.rows){ rect.y = temp_mat.rows; }

  127. if (rect.x + rect.width > temp_mat.cols){ rect.width = temp_mat.cols - rect.x; }

  128. if (rect.y + rect.height > temp_mat.rows){ rect.height = temp_mat.rows - rect.y; }

  129.  
  130. cv::Mat face_cut(temp_mat, rect);

你可能感兴趣的:(人脸裁剪)