【OpenCV+Dlib】C++实现人脸几何矫正(保持头部水平且大小固定)原理+源码

阅读: 评论:0

【OpenCV+Dlib】C++实现人脸几何矫正(保持头部水平且大小固定)原理+源码

【OpenCV+Dlib】C++实现人脸几何矫正(保持头部水平且大小固定)原理+源码

在图像实际应用过程中,在很多情况下我们需要截取下人脸的某些位置,且保证被截取位置大小方向固定,以眼睛为例,如果要保证截取的眼睛大小固定则需要保持两眼中心的连线水平且距离固定。在视频流的情况下,随着人头部的晃动和头部距离摄像头的远近,眼睛的方向、大小时刻在发生变化,此时便需要进行人脸几何校正(根据头部的晃动角度旋转图像并缩放图像),保证在图像中人的眼睛的大小及方向是确定的,从而能够截取到人脸准确的位置。

本文所做的人脸几何校正是基于opencv和dlib的,opencv和dlib的配置可参考以下两个博客:
Opencv的安装与配置:博客链接
Dlib的安装与配置:博客链接
首先我们需要使用dlib进行人脸的68个关键点的定位,(关键点的定位可以参考我的这篇博客博客链接)根据关键点为位置,进行图像旋转等操作

将点36和点45的中心点做为旋转点
记点36到45的距离为:r=sqrt( (x_45 - x_36)2+(y_45 - y_36)2 )
图中L的长度为:L=x_45 - x_36
图中h的高度为:h=y_45 - y_36

根据几何原理知:sin a a a=r/h
所以有: a a a=arcsin( r / h)
将点39和点42的中心点作为旋转点,记为:P (x_rotating , y_rotating)
则有:
x_rotating=(x_39 + x_42) / 2;
y_rotating=(y_39 + y_42) / 2;

几何校正效果图如下:

几何校正源码如下:
源码中以截取两个眼睛的图像为例,在经过旋转和缩放后,可以截取人脸任意位置的图像且大小固定,不受人脸角度和人脸距离相机距离的影响

#include <dlibopencv.h>
#include <opencv2opencv.hpp>
#include <dlibimage_processingfrontal_face_detector.h>
#include <dlibimage_processingrender_face_detections.h>
#include <dlibimage_processing.h>
#include <dlibgui_widgets.h>
#include <iostream>
#include <vector>
#include <cmath>
#include<time.h>
#include<math.h>#define DISTANCE 90			//点36到点42的固定距离using namespace std;
using namespace dlib;
using namespace cv;//opencv实现图片的任意角度旋转
//逆时针旋转图像degree角度(原尺寸)
Mat rotateImage(Mat src, int angle, int x_rotating, int y_rotating)
{//旋转中心为图像中心Mat srcImage = src;Mat	rotate_dstImage;Mat rot_mat(2, 3, CV_32FC1);Point center = Point(x_rotating, y_rotating);double scale = 0.6;//计算旋转矩阵rot_mat = getRotationMatrix2D(center, angle, 1);//旋转图像warpAffine(srcImage, rotate_dstImage, rot_mat, srcImage.size());return rotate_dstImage;
}int main() {Mat right_roi;		//声明一个固定大小的图像,用于存储左眼图像Mat left_roi;		//声明一个固定大小的图像,用于存储右眼图像Mat left_eye_copy;	//保存左眼图像Mat right_eye_copy;	//保存右眼图像try {VideoCapture cap(0);if (!cap.isOpened()) {printf("Unable to connect a camera!");return 1;}frontal_face_detector detector = get_frontal_face_detector();shape_predictor pos_modle;deserialize("D:/shape_predictor_68_face_landmarks/shape_predictor_68_face_landmarks.dat") >> pos_modle;while (waitKey(30) != 27) {Mat temp;Mat cv_temp;			//后边的操作将temp转化为dilb的类型,opencv不能处理,声明一个与temp一样的cv_tempMat rotate_temp;		//旋转后的图像Mat scaling_temp;		//缩放后的图像cap >> temp;cv_temp = temp.clone();		//保存opencv类型的图像//将图像转化为dlib中的BGR的形式cv_image<bgr_pixel> cimg(temp);std::vector<dlib::rectangle> faces = detector(cimg);std::vector<full_object_detection> shapes;unsigned int faceNumber = faces.size();   //获取容器中向量的个数即人脸的个数for (unsigned int i = 0; i < faceNumber; i++) {shapes.push_back(pos_modle(cimg, faces[i]));}if (!pty()) {int faceNumber = shapes.size();for (int j = 0; j < faceNumber; j++){for (int i = 0; i < 68; i++){//用来画特征值的点cv::circle(temp, cvPoint(shapes[j].part(i).x(), shapes[j].part(i).y()), 1, cv::Scalar(0, 0, 255), -1);//参数说明 图像 圆心 线条宽度 颜色 线的类型//显示数字//cv::putText(temp, to_string(i), cvPoint(shapes[0].part(i).x(), shapes[0].part(i).y()), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255));}}//左眼关键点int x_36 = shapes[0].part(36).x();int y_36 = shapes[0].part(36).y();int x_37 = shapes[0].part(37).x();int y_37 = shapes[0].part(37).y();int x_38 = shapes[0].part(38).x();int y_38 = shapes[0].part(38).y();int x_39 = shapes[0].part(39).x();int y_39 = shapes[0].part(39).y();int x_40 = shapes[0].part(40).x();int y_40 = shapes[0].part(40).y();int x_41 = shapes[0].part(41).x();int y_41 = shapes[0].part(41).y();int left_length = x_38 - x_37;//右眼关键点int x_42 = shapes[0].part(42).x();int y_42 = shapes[0].part(42).y();int x_43 = shapes[0].part(43).x();int y_43 = shapes[0].part(43).y();int x_44 = shapes[0].part(44).x();int y_44 = shapes[0].part(44).y();int x_45 = shapes[0].part(45).x();int y_45 = shapes[0].part(45).y();int x_46 = shapes[0].part(46).x();int y_46 = shapes[0].part(46).y();int x_47 = shapes[0].part(47).x();int y_47 = shapes[0].part(47).y();int right_length = x_44 - x_43;//人脸几何校正(保持双眼连线水平)//旋转中心点为两眼角点(36和45)的中心位置float x_rotating = (x_39 + x_42) / 2;		//旋转中心点横坐标float y_rotating = (y_39 + y_42) / 2;		//旋转中心点纵坐标//计算旋转角度alphafloat r = (x_45 - x_36) * (x_45 - x_36) + (y_45 - y_36) * (y_45 - y_36);r = sqrt(r);float l = x_45 - x_36;float h = y_45 - y_36;double alpha;if (h < 0) {		//人脸像左倾斜//h = 0 - h;double r_h = h / r;alpha = (double)((asin(r_h) * 180) / CV_PI);	//alpha=arcsin(r/h)alpha = 360 + alpha;}else {				//人脸向右倾斜double r_h = h / r;alpha = (double)((asin(r_h) * 180) / CV_PI);	//alpha=arcsin(r/h)}//cout << "r=" << r << endl;//cout << "l=" << l << endl;//cout << "h=" << h << endl;//cout << "alpha=" << alpha << endl;//旋转图像cvtColor(cv_temp, cv_temp, COLOR_BGR2GRAY);rotate_temp = rotateImage(cv_temp, alpha, x_rotating, y_rotating);//重新定位点36,37,42,43变换后的位置//36,37,42,43点的X坐标没有改变int hight_36_37 = y_36 - y_37;int hight_42_43 = y_42 - y_43;double length_36_rotating = (x_rotating - x_36) * (x_rotating - x_36) + (y_rotating - y_36) * (y_rotating - y_36);length_36_rotating = sqrt(length_36_rotating);double length_42_rotating = (x_rotating - x_42) * (x_rotating - x_42) + (y_rotating - y_42) * (y_rotating - y_42);length_42_rotating = sqrt(length_42_rotating);//36,42的纵坐标与旋转点的纵坐标一样y_36 = y_rotating;y_42 = y_rotating;y_37 = y_36 - hight_36_37;y_43 = y_42 - hight_42_43;//重新定位横坐标x_36 = (int)x_rotating - (int)length_36_rotating;x_42 = (int)x_rotating + (int)length_42_rotating;//imshow("rotate_temp", rotate_temp);//固定下点36和点42的距离//等比例缩放图像int distance = x_42 - x_36;double scaling;		//缩放比例scaling = distance / (double)DISTANCE;		//DISTANCE宏定义为90,//cout << "scaling:" << scaling << endl;if (scaling <= 1) {scaling = 1 + (1 - scaling);}else {scaling = 1 - (scaling - 1);}//根据缩放的比例缩放图像resize(rotate_temp, scaling_temp, Size((int)(ls * scaling), (int)(ws * scaling)), 0, 0, INTER_LINEAR);		imshow("scaling_temp", scaling_temp);cout << "distance:" << distance << endl;//再次定位点36,37,42,43,rotating变换后的位置,用于截取眼睛图像x_rotating = x_rotating * scaling;y_rotating = y_rotating * scaling;x_36 = x_rotating - (length_36_rotating * scaling);y_36 = y_rotating;y_37 = y_36 - (hight_36_37 * scaling);x_42 = x_rotating + (length_42_rotating * scaling);y_42 = y_rotating;y_43 = y_42 - (hight_42_43 * scaling);int distance_36_42 = x_42 - x_36;	//cout << "distance_36_42:" << distance_36_42 << endl;//复制左眼的部分的图像left_roi = scaling_temp(Rect(x_36 - 10, y_37 - 5, 50, 20));left_eye_copy = Mat(Size(50, 20), pe());left_eye_copy = Scalar(255, 255, 255);     //给图像填充颜色left_eye_copy = left_roi.clone();imshow("left_eye_copy", left_eye_copy);//复制右眼的部分的图像right_roi = scaling_temp(Rect(x_42 - 5, y_43 - 5, 50, 20));right_eye_copy = Mat(Size(50, 20), pe());right_eye_copy = Scalar(255, 255, 255);     //给图像填充颜色right_eye_copy = right_roi.clone();imshow("right_eye_copy", right_eye_copy);cv::imshow("Dlib标记", temp);}}}catch (serialization_error& e) {cout << "You need dlib‘s default face landmarking file to run this example." << endl;cout << endl << e.what() << endl;}catch (exception& e) {cout << e.what() << endl;}}

本文发布于:2024-01-28 12:32:09,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17064163337438.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:头部   几何   源码   原理   大小
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23