之前写了一篇
利用OpenCV做个熊猫表情包吧_Leen的博客-CSDN博客
回想起来觉得有点太弱了,意犹未尽,每次使用需要自己去手动截取人脸,清除黑边什么的才能使用demo去合成表情,无奈之前由于安装的vs,opencv版本都比较低,也懒得再折腾。
恰逢前些天电脑硬盘坏了,数据丢了,一切都要重装,那直接高配走起,VS2022+OpenCV4.8,既然环境都有了,于是有空的时候就改进了一下,让它利用opencv,做简单的人脸识别,自动去图片中识别、提取人脸,同时去做黑边清理工作,自动化程度更高,用起来更省事儿~
原理呢就是在处理原始图片的流程中加入了面部识别,将面部单独切出来,同时对面部图片做黑边清晰处理,然后再进行表情的合成工作,下面介绍一下具体过程:
首先是识别到用户输入的原图
利用opencv进行面部识别
灰度化图片后提取面部,并清理黑边
再将面部跟熊猫脸进行融合
下面介绍关键步骤的代码:
初始化面部识别
- int InitFaceDetect()
- {
- if (!faceCascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml")) {
- cout << "人脸检测级联分类器没找到!!" << endl;
- return -1;
- }
- if (!eyes_Cascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml")) {
- cout << "眼睛检测级联分类器没找到!!" << endl;
- return -1;
- }
-
- return 0;
- }
用到的两个xml特征文件均为openCV提供。
清楚灰度图像中的深色边角区域
- /************************************************************************/
- /* 消除图片四周的黑色边角区域 */
- /************************************************************************/
-
- Mat RemoveBlackCorner(Mat img)
- {
- int i, j;
- int h = img.size().height;
- int w = img.size().width;
-
- if (img.channels() == 1) //灰度图片
- {
- for (j = 0; j < h; j++)
- {
- for (i = 0; i < w; i++)
- {
- if (img.at
(j, i) < 110) - {
- img.at
(j, i) = 255; - }
- else
- {
- break;
- }
- }
- for (i = w - 1; i >= 0; i--)
- {
- if (img.at
(j, i) < 110) - {
- img.at
(j, i) = 255; - }
- else
- {
- break;
- }
- }
- }
- for (i = 0; i < w; i++)
- {
- for (j = 0; j < h; j++)
- {
- if (img.at
(j, i) < 110) - {
- img.at
(j, i) = 255; - }
- else
- {
- break;
- }
- }
- for (j = h - 1; j >= 0; j--)
- {
- if (img.at
(j, i) < 110) - {
- img.at
(j, i) = 255; - }
- else
- {
- break;
- }
- }
- }
- }
-
- return img;
- }
-
-
人脸识别以及将加工后的人脸存成临时文件
- bool parse_cmd(int argc, char* argv[])
- {
- if (argc < 3)
- {
- return false;
- }
-
- g_str_src = string(argv[1]);
- g_str_bg = string(argv[2]);
-
- return true;
- }
-
- string GetFolderFromFile(string strFile)
- {
- size_t last_slash = strFile.find_last_of("\\");
- std::string directory = strFile.substr(0, last_slash);
- return directory;
- }
-
-
- int DetectFace(Mat img, Mat imgGray) {
- namedWindow("src", WINDOW_AUTOSIZE);
- vector
faces, eyes; - faceCascade.detectMultiScale(imgGray, faces, 1.2, 5, 0, Size(30, 30));
- int retVal = -1;
- //目前只取一个脸
- if (faces.size() > 0) {
- for (size_t i = 0; i < faces.size(); i++) {
- //框出人脸位置
- rectangle(img, Point(faces[i].x+ faces[i].width / 8, faces[i].y+faces[i].height / 8),
- Point(faces[i].x + faces[i].width*7/8, faces[i].y + faces[i].height * 7 / 8), Scalar(0, 0, 255), 1, 8);
- cout << faces[i] << endl;
- //将人脸从灰度图中抠出来
- Mat face_ = imgGray(faces[i]);
- //缩小一点,默认取的矩形比较大
- Rect rect(Point(faces[i].width / 8, faces[i].height / 8),
- Point(faces[i].width * 7 / 8, faces[i].height * 7/ 8));
- Mat ROI = face_(rect);
- //RemoveBlackBorder(ROI, ROI);
- Mat imgOut = RemoveBlackCorner(ROI);
- //RemoveBlackBorder(ROI, imgOut);
- imwrite(g_str_face, imgOut);
- retVal = 0;
- eyes_Cascade.detectMultiScale(face_, eyes, 1.2, 2, 0, Size(30, 30));
- for (size_t j = 0; j < eyes.size(); j++) {
- Point eye_center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
- int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);
- circle(img, eye_center, radius, Scalar(65, 105, 255), 4, 8, 0);
- }
- }
- }
- imshow("src", img);
- return retVal;
- }
主逻辑流程
-
- int main(int argc, char* argv[])
- {
- if (!parse_cmd(argc, argv))
- {
- cout << "command error" << endl;
- return -1;
- }
-
- if (InitFaceDetect() != 0)
- {
- return -1;
- }
-
- //
- string strDirBase = GetFolderFromFile(g_str_src);
- Mat img_src = imread(g_str_src);
- Mat img_background = imread(g_str_bg);
- g_str_face = strDirBase + "\\tmp_face.jpg";
- #ifdef _DBG_SHOW
- namedWindow("img_src");
- imshow("img_src", img_src);
- #endif
-
- Mat img_gray;
- cvtColor(img_src, img_gray, COLOR_BGR2GRAY); //图像灰度化
-
- int nFace = DetectFace(img_src, img_gray);
- waitKey(3000);
- #ifdef _DBG_SHOW
- namedWindow("gray", WINDOW_NORMAL);
- imshow("gray", img_gray);
- #endif
- // 按照背景图大小等比缩放
- Size dsize = Size(img_background.cols * 0.55, img_background.rows * 0.55);
- //判断一下是否自动检测到了人脸
- Mat img_face;
- if (nFace == 0)
- {
- cout << "opencv find face,get face." << endl;
- img_face = imread(g_str_face);
- }
- else
- {
- cout << "can not find face.use image user input." << endl;
- img_face = img_gray;
- }
- resize(img_face, img_face, dsize, 1, 1, INTER_AREA);
-
- //输出缩放后效果图并重新加载
- Mat img_face2;
- threshold(img_face, img_face2, 105, 255, THRESH_BINARY);
- imwrite(strDirBase + "\\tmp.jpg", img_face2);
- //imshow("img_face2", img_face2);
- Mat img_face3 = imread(strDirBase + "\\tmp.jpg");
- //居中粘合两图
- Rect roi_rect = Rect((img_background.cols - img_face3.cols) / 2, (img_background.rows - img_face3.rows) / 2
- , img_face3.cols, img_face3.rows);
- img_face3.copyTo(img_background(roi_rect));
- //显示并输出
- imshow("mixed", img_background);
-
- imwrite(g_str_src + ".emoji.jpg", img_background);
-
- waitKey(5000);
-
- destroyAllWindows();
- return 0;
- }