上一篇已经把人脸的脸部区域框出来了,那么要把人脸抠出来,只需要把轮廓的特征点放在一个verctor中组成一个ROI区域,然后根据OpenCV多边形抠图原理,把人脸抠出来就可以了。注意一下,把特征点放入的时候需要顺序加进vertor中,不然抠出的区域会出现会有交叉。
下面来看看代码:
void Detection( std::vector<cv::String> files) {
LBFRegressor regressor;
extern string cascadeName;
CascadeClassifier cascade;
double scale = 1.0;
int i = 0;
regressor.Load(modelPath + "LBF.model");
// -- 1. Load the cascades
if (!cascade.load(cascadeName)) {
cerr << "ERROR: Could not load classifier cascade" << endl;
}
const static Scalar colors[] = { CV_RGB(0,0,255),
CV_RGB(0,128,255),
CV_RGB(0,255,255),
CV_RGB(0,255,0),
CV_RGB(255,128,0),
CV_RGB(255,255,0),
CV_RGB(255,0,0),
CV_RGB(255,0,255) };
int count = files.size();
for (int j = 0; j < count; j++)
{
Mat gray;
double t = 0;
vector<Rect> faces;
//vector<double> polygonX;//多边形轮廓点集合
//vector<double> polygonY;
vector<Point> polygon;
Mat img = imread(files[j]);
cvtColor(img, gray, CV_BGR2GRAY);
// --Detection
t = (double)cvGetTickCount();
//cascade.detectMultiScale(gray, faces,
// 1.1, 2, 0
// //|CV_HAAR_FIND_BIGGEST_OBJECT
// //|CV_HAAR_DO_ROUGH_SEARCH
// | CV_HAAR_SCALE_IMAGE
// ,
// Size(30, 30));
cascade.detectMultiScale(gray, faces, 1.3, 1, 0, Size(30, 30));
t = (double)cvGetTickCount() - t;
printf("detection time = %g ms\n", t / ((double)cvGetTickFrequency()*1000.));
// --Alignment
t = (double)cvGetTickCount();
//检测到超过一个人脸,则只计算一个人脸的特征,因为送入的图片本身就只包含一张人脸,检测出现两个
//人脸,则检测出错,所以加facesize,保证只检测大的人脸特征点。不然特征点会多于18个,绘制的roi中间
//会被挖空。
//for (vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++) {
for (vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r= faces.end(), i++) {
Point center;
Scalar color = colors[i % 8];
BoundingBox boundingbox;
boundingbox.start_x = r->x*scale;
boundingbox.start_y = r->y*scale;
boundingbox.width = (r->width - 1)*scale;
boundingbox.height = (r->height - 1)*scale;
boundingbox.centroid_x = boundingbox.start_x + boundingbox.width / 2.0;
boundingbox.centroid_y = boundingbox.start_y + boundingbox.height / 2.0;
t = (double)cvGetTickCount();
Mat_<double> current_shape = regressor.Predict(gray, boundingbox, 1);
t = (double)cvGetTickCount() - t;
printf("alignment time = %g ms\n", t / ((double)cvGetTickFrequency()*1000.));
// // draw bounding box
// rectangle(img, cvPoint(boundingbox.start_x,boundingbox.start_y),
// cvPoint(boundingbox.start_x+boundingbox.width,boundingbox.start_y+boundingbox.height),Scalar(0,255,0), 1, 8, 0);
// draw result :: red
double deta = current_shape(0, 0) - current_shape(15, 0);
double rec_point1_y = current_shape(0, 1) + deta / 2;//第0个特征点与第15个特征点的差值的一般作为矩形的宽
double rec_point1_x = current_shape(0, 0);
//如果超过则赋值为0,到图片边界
if (rec_point1_x < 0) { rec_point1_x = 0.0; }
if (rec_point1_y < 0) { rec_point1_y = 0.0; }
Point rec_point1 = Point(rec_point1_x, rec_point1_y);//对角线两个点
Point rec_point2 = Point(current_shape(16, 0), current_shape(16, 1));
Point rec_point3 = Point(rec_point1_x, current_shape(16, 1));
Point rec_point4 = Point(current_shape(16, 0), rec_point1_y);
polygon.push_back(rec_point1);
polygon.push_back(rec_point3);
//cout << deta << endl;
//cout << rec_point1 << endl;
//cout << rec_point2<< endl;
//rectangle(img, rec_point1, rec_point2, Scalar(255, 255, 0), 2, 8, 0);
for (int i = 0; i <16 /*global_params.landmark_num*/; i++) {
//circle(img, Point2d(current_shape(i, 0), current_shape(i, 1)), 3, Scalar(255, 255, 255), -1, 8, 0);
//cout << current_shape(i, 0) << endl;
//cout << current_shape(i, 1) << endl;
//cout << global_params.landmark_num << endl;
Point pp = Point (current_shape(i, 0), current_shape(i, 1));
Point pp2 = Point (current_shape(i+1, 0), current_shape(i+1, 1));
//putText(img, std::to_string(i), pp, FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255),1, 4);//在图片上写文字
//cout << std::to_string(i)<<pp << endl;
//cout << pp << endl;
//line(img, pp,pp2, Scalar(255, 255, 0),2);
//imshow("底板", img);
//polygonX.push_back(current_shape(i, 0));//添加多边形轮廓点
//polygonY.push_back(current_shape(i, 1));//添加多边形轮廓点
polygon.push_back(pp);
//cout << polygonX[i] << endl;
//waitKey(300);
}
//double deta = current_shape(0, 0) - current_shape(15, 0);
//double rec_point1_y = current_shape(0, 1) + deta / 2;//第0个特征点与第15个特征点的差值的一般作为矩形的宽
//double rec_point1_x = current_shape(0, 0);
////如果超过则赋值为0,到图片边界
//if (rec_point1_x < 0) { rec_point1_x = 0.0; }
//if (rec_point1_y < 0) { rec_point1_y = 0.0; }
//Point rec_point1 = Point(rec_point1_x, rec_point1_y);//对角线两个点
//Point rec_point2 = Point(current_shape(16, 0), current_shape(16, 1));
//Point rec_point3 = Point(rec_point1_x, current_shape(16, 1));
//Point rec_point4 = Point(current_shape(16, 0), rec_point1_y);
////cout << deta << endl;
////cout << rec_point1 << endl;
////cout << rec_point2<< endl;
//rectangle(img, rec_point1,rec_point2,Scalar(255,255,0), 2, 8, 0);
//line(img, Point(current_shape(36,0),current_shape(36,1)), Point(current_shape(45,0),current_shape(45,1)), Scalar(255, 255, 0), 2);
//cv::imshow("result", img);
//waitKey(300);
polygon.push_back(rec_point2);
polygon.push_back(rec_point4);
//cout << files[j] << endl;
string depthFace_dir, RGBFace_dir;
string roi_depthFace_dir, roi_RGBFace_dir;//抠出的人脸轮廓
//depthFace_dir = files[j];//RGB图片绝对路径
RGBFace_dir = files[j];//RGB图片绝对路径
//depthFace_dir.replace()
depthFace_dir = string_replace(RGBFace_dir, "RGBFace", "DepthFace");
depthFace_dir = string_replace(depthFace_dir, "jpg", "png");
Mat Depth_image = imread(depthFace_dir, CV_LOAD_IMAGE_UNCHANGED);//读取CV_16UC1类型的数据
//cout << depthFace_dir << endl;
if (!Depth_image.empty()) {
Mat roi_depthFace = contour_roi(Depth_image, polygon);
//新建一个roi_depthFace_dir文件夹,用于保存处理后的深度图片
roi_depthFace_dir = string_replace(depthFace_dir, "DepthFace", "roi_DepthFace");
//cout << roi_depthFace_dir << endl;
imwrite(roi_depthFace_dir, roi_depthFace);
Mat roi_RGBFace = contour_roi(img, polygon);
//新建一个roi_RGBFace_dir文件夹,用于保存处理后的RGB图片
//保存这个图是为了方便删除抠出的人脸是无效的,可以直接删除
roi_RGBFace_dir = string_replace(RGBFace_dir, "RGBFace", "roi_RGBFace");
//cout << roi_RGBFace_dir << endl;
imwrite(roi_RGBFace_dir, roi_RGBFace);
}
else {
cout << depthFace_dir << endl;
ofstream out;
out.open("E:/学习/深度学习/活体检测/数据集/Captrue_Data/dataset/real/read_image_emtpy.txt",ios::out);
out << depthFace_dir << "\n";
continue;
}
}
//cv::imshow("result", img);
//waitKey(10);
//Mat image = imread("E:/201842516518997.png", CV_LOAD_IMAGE_UNCHANGED);//CV_16UC1类型的数据
//读取16位的深度图
//waitKey(100);
//waitKey(100);
}
}
其中读取16位深度图
Mat image = imread(“E:/201842516518997.png”,
CV_LOAD_IMAGE_UNCHANGED);//CV_16UC1类型的数据
//读取16位的深度图
特别注意,一定要将点按顺序放入vector中。
还用到了其他几个函数,一并放这里吧。
参数分别是图片和轮廓点的集合
返回值是抠出的区域图像。
cv::Mat contour_roi(cv::Mat img,std::vector<Point> polygon) {
Mat dst;
Mat roi = Mat::zeros(img.size(), CV_8U);
contour.push_back(polygon);
// 画出
drawContours(roi, contour, 0, Scalar::all(255), -1);
img.copyTo(dst, roi);
imshow("roi", roi);
//waitKey(100);
//imshow("img", img);
//waitKey(100);
//imshow("dst", dst);
//waitKey(100);
waitKey(10);
return dst;
}
字符替换函数
//************************************
// Method: string_replace
// FullName: string_replace
// Access: public
// Returns: void
// Qualifier: 把字符串的strsrc替换成strdst
// Parameter: std::string & strBig
// Parameter: const std::string & strsrc
// Parameter: const std::string & strdst
//************************************
std::string string_replace(std::string strBig, const std::string &strsrc, const std::string &strdst)
{
std::string::size_type pos = 0;
std::string::size_type srclen = strsrc.size();
std::string::size_type dstlen = strdst.size();
while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
{
strBig.replace(pos, srclen, strdst);
pos += dstlen;
}
return strBig;
}
人脸区域抠出来的效果图
测试了一下时间,看看速度怎么样
这种方法对正脸效果很好,但是对于侧脸效果一般。
抠人脸主要是为了去掉深度摄像头获取的深度图在人脸周围的毛刺,如下图。
从深度摄像头获取的深度图
通过初步处理之后
但是还是很多毛刺。
抠出人脸区域之后
通过进一步处理
很干净了。处理方法在后续博客会更新。
C++代码后续也会上传到github上。