• 利用OpenCV做个熊猫表情包 二


    之前写了一篇

    利用OpenCV做个熊猫表情包吧_Leen的博客-CSDN博客

    回想起来觉得有点太弱了,意犹未尽,每次使用需要自己去手动截取人脸,清除黑边什么的才能使用demo去合成表情,无奈之前由于安装的vs,opencv版本都比较低,也懒得再折腾。

    恰逢前些天电脑硬盘坏了,数据丢了,一切都要重装,那直接高配走起,VS2022+OpenCV4.8,既然环境都有了,于是有空的时候就改进了一下,让它利用opencv,做简单的人脸识别,自动去图片中识别、提取人脸,同时去做黑边清理工作,自动化程度更高,用起来更省事儿~

    原理呢就是在处理原始图片的流程中加入了面部识别,将面部单独切出来,同时对面部图片做黑边清晰处理,然后再进行表情的合成工作,下面介绍一下具体过程:

    首先是识别到用户输入的原图

    利用opencv进行面部识别

    灰度化图片后提取面部,并清理黑边

    再将面部跟熊猫脸进行融合

    下面介绍关键步骤的代码:

    初始化面部识别

    1. int InitFaceDetect()
    2. {
    3. if (!faceCascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_frontalface_default.xml")) {
    4. cout << "人脸检测级联分类器没找到!!" << endl;
    5. return -1;
    6. }
    7. if (!eyes_Cascade.load("D:\\Workspace\\opencv\\build\\etc\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml")) {
    8. cout << "眼睛检测级联分类器没找到!!" << endl;
    9. return -1;
    10. }
    11. return 0;
    12. }

    用到的两个xml特征文件均为openCV提供。

    清楚灰度图像中的深色边角区域

    1. /************************************************************************/
    2. /* 消除图片四周的黑色边角区域 */
    3. /************************************************************************/
    4. Mat RemoveBlackCorner(Mat img)
    5. {
    6. int i, j;
    7. int h = img.size().height;
    8. int w = img.size().width;
    9. if (img.channels() == 1) //灰度图片
    10. {
    11. for (j = 0; j < h; j++)
    12. {
    13. for (i = 0; i < w; i++)
    14. {
    15. if (img.at(j, i) < 110)
    16. {
    17. img.at(j, i) = 255;
    18. }
    19. else
    20. {
    21. break;
    22. }
    23. }
    24. for (i = w - 1; i >= 0; i--)
    25. {
    26. if (img.at(j, i) < 110)
    27. {
    28. img.at(j, i) = 255;
    29. }
    30. else
    31. {
    32. break;
    33. }
    34. }
    35. }
    36. for (i = 0; i < w; i++)
    37. {
    38. for (j = 0; j < h; j++)
    39. {
    40. if (img.at(j, i) < 110)
    41. {
    42. img.at(j, i) = 255;
    43. }
    44. else
    45. {
    46. break;
    47. }
    48. }
    49. for (j = h - 1; j >= 0; j--)
    50. {
    51. if (img.at(j, i) < 110)
    52. {
    53. img.at(j, i) = 255;
    54. }
    55. else
    56. {
    57. break;
    58. }
    59. }
    60. }
    61. }
    62. return img;
    63. }

    人脸识别以及将加工后的人脸存成临时文件

    1. bool parse_cmd(int argc, char* argv[])
    2. {
    3. if (argc < 3)
    4. {
    5. return false;
    6. }
    7. g_str_src = string(argv[1]);
    8. g_str_bg = string(argv[2]);
    9. return true;
    10. }
    11. string GetFolderFromFile(string strFile)
    12. {
    13. size_t last_slash = strFile.find_last_of("\\");
    14. std::string directory = strFile.substr(0, last_slash);
    15. return directory;
    16. }
    17. int DetectFace(Mat img, Mat imgGray) {
    18. namedWindow("src", WINDOW_AUTOSIZE);
    19. vector faces, eyes;
    20. faceCascade.detectMultiScale(imgGray, faces, 1.2, 5, 0, Size(30, 30));
    21. int retVal = -1;
    22. //目前只取一个脸
    23. if (faces.size() > 0) {
    24. for (size_t i = 0; i < faces.size(); i++) {
    25. //框出人脸位置
    26. rectangle(img, Point(faces[i].x+ faces[i].width / 8, faces[i].y+faces[i].height / 8),
    27. Point(faces[i].x + faces[i].width*7/8, faces[i].y + faces[i].height * 7 / 8), Scalar(0, 0, 255), 1, 8);
    28. cout << faces[i] << endl;
    29. //将人脸从灰度图中抠出来
    30. Mat face_ = imgGray(faces[i]);
    31. //缩小一点,默认取的矩形比较大
    32. Rect rect(Point(faces[i].width / 8, faces[i].height / 8),
    33. Point(faces[i].width * 7 / 8, faces[i].height * 7/ 8));
    34. Mat ROI = face_(rect);
    35. //RemoveBlackBorder(ROI, ROI);
    36. Mat imgOut = RemoveBlackCorner(ROI);
    37. //RemoveBlackBorder(ROI, imgOut);
    38. imwrite(g_str_face, imgOut);
    39. retVal = 0;
    40. eyes_Cascade.detectMultiScale(face_, eyes, 1.2, 2, 0, Size(30, 30));
    41. for (size_t j = 0; j < eyes.size(); j++) {
    42. Point eye_center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
    43. int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);
    44. circle(img, eye_center, radius, Scalar(65, 105, 255), 4, 8, 0);
    45. }
    46. }
    47. }
    48. imshow("src", img);
    49. return retVal;
    50. }

    主逻辑流程

    1. int main(int argc, char* argv[])
    2. {
    3. if (!parse_cmd(argc, argv))
    4. {
    5. cout << "command error" << endl;
    6. return -1;
    7. }
    8. if (InitFaceDetect() != 0)
    9. {
    10. return -1;
    11. }
    12. //
    13. string strDirBase = GetFolderFromFile(g_str_src);
    14. Mat img_src = imread(g_str_src);
    15. Mat img_background = imread(g_str_bg);
    16. g_str_face = strDirBase + "\\tmp_face.jpg";
    17. #ifdef _DBG_SHOW
    18. namedWindow("img_src");
    19. imshow("img_src", img_src);
    20. #endif
    21. Mat img_gray;
    22. cvtColor(img_src, img_gray, COLOR_BGR2GRAY); //图像灰度化
    23. int nFace = DetectFace(img_src, img_gray);
    24. waitKey(3000);
    25. #ifdef _DBG_SHOW
    26. namedWindow("gray", WINDOW_NORMAL);
    27. imshow("gray", img_gray);
    28. #endif
    29. // 按照背景图大小等比缩放
    30. Size dsize = Size(img_background.cols * 0.55, img_background.rows * 0.55);
    31. //判断一下是否自动检测到了人脸
    32. Mat img_face;
    33. if (nFace == 0)
    34. {
    35. cout << "opencv find face,get face." << endl;
    36. img_face = imread(g_str_face);
    37. }
    38. else
    39. {
    40. cout << "can not find face.use image user input." << endl;
    41. img_face = img_gray;
    42. }
    43. resize(img_face, img_face, dsize, 1, 1, INTER_AREA);
    44. //输出缩放后效果图并重新加载
    45. Mat img_face2;
    46. threshold(img_face, img_face2, 105, 255, THRESH_BINARY);
    47. imwrite(strDirBase + "\\tmp.jpg", img_face2);
    48. //imshow("img_face2", img_face2);
    49. Mat img_face3 = imread(strDirBase + "\\tmp.jpg");
    50. //居中粘合两图
    51. Rect roi_rect = Rect((img_background.cols - img_face3.cols) / 2, (img_background.rows - img_face3.rows) / 2
    52. , img_face3.cols, img_face3.rows);
    53. img_face3.copyTo(img_background(roi_rect));
    54. //显示并输出
    55. imshow("mixed", img_background);
    56. imwrite(g_str_src + ".emoji.jpg", img_background);
    57. waitKey(5000);
    58. destroyAllWindows();
    59. return 0;
    60. }

  • 相关阅读:
    【数据结构与算法】线性表的定义和基本操作
    ssm整合增删改查
    maven项目子类项目版本与父类版本不一致
    【1day】复现宏景OA KhFieldTree接口 SQL注入漏洞
    传统考勤太复杂怎么办?这个小技巧,我必须吹爆!
    第一章 C语言知识点(程序)
    原子操作 与 竞争冒险
    CMake教程 - basic point
    python中elasticsearch_dsl模块用法详解
    大数据技术之Hive:先导篇(一)
  • 原文地址:https://blog.csdn.net/linlin003/article/details/134446555