C++开发人脸性别识别教程(18)——辅助功能之文件名批量修改、方法验证

深藏阁楼爱情的钟 2022-08-20 13:15 249阅读 0赞

  时光推移了30多天,这个人脸性别识别的小项目也接近尾声了,预计再通过三篇博文的篇幅来完成这个项目的收尾工作。在这篇博文中我们再为程序添加另外两个小的辅助功能:文件名批量修改、方法验证。

  一 文件名批量修改

  批量修改文件名是一件很基础也很常用的小操作,核心操作就是图像文件的批量读取、批量改名、批量保存。基本思想就是把文件读出来,然后在保存回去(注意不要和别的文件发生覆盖),从这个角度来讲文件名批量修改与上一篇博客C++开发人脸性别识别教程(17)——辅助功能之人脸批量分割中的人脸批量分割简直如出一辙,这里就不再赘述,大家请自行添加控件按钮、编写相关函数吧:

589850-20160229155808111-1217667554.png

  二 方法验证

  这个小功能是当初为了测试四种分类方法(PCA、Fisher、LBP、HOG+SVM)的分类性能而额外添加的一段代码,目的很简单,就是想看看四种方法分类时哪个方法更准确一点。

  2.1 添加控件

  首先,添加一个按钮控件,命名为“方法验证”,ID不用变:

589850-20160229161304517-1497243422.png

  然后在添加两个编辑框控件,用来显示各个方法的分类正确率,ID分别更改为IDC_Effect_Man和IDC_Effect_Wom:

589850-20160229161957533-429443125.png

  2.2 批量读取测试样本

  双击“方法验证”按钮,添加对应的事件处理函数OnBnClickedButton3(),首先,读取测试样本所在的文件夹:

  1. CString str; //存储图像路径
  2. BROWSEINFO bi; //用来存储用户选中的目录信息
  3. TCHAR name[MAX_PATH]; //存储路径
  4. char imageFullName[500]; //存储单个图像文件的全路径
  5. name[0]='d';
  6. ZeroMemory(&bi,sizeof(BROWSEINFO)); //清空目录对应的内存
  7. bi.hwndOwner=GetSafeHwnd(); //得到窗口句柄
  8. bi.pszDisplayName=name;
  9. BIF_BROWSEINCLUDEFILES;
  10. bi.lpszTitle=_T("Select folder"); //对话框标题
  11. bi.ulFlags=0x80;//设置对话框形式
  12. LPITEMIDLIST idl=SHBrowseForFolder(&bi);//返回所选中文件夹的ID
  13. if(idl==NULL)
  14. return;
  15. SHGetPathFromIDList(idl,str.GetBuffer(MAX_PATH));//将文件信息格式化存储到对应缓冲区中
  16. str.ReleaseBuffer(); //与GerBuffer配合使用,清空内存
  17. m_Path=str; //将路径存储在m_path中
  18. if(str.GetAt(str.GetLength()-1)!='\\')
  19. m_Path+="\\";
  20. UpdateData(FALSE);
  21. // free memory used
  22. IMalloc * imalloc = 0;
  23. if (SUCCEEDED(SHGetMalloc(&imalloc)))
  24. {
  25. imalloc->Free (idl);
  26. imalloc->Release();
  27. }
  28. m_ImageDir=(LPSTR)(LPCTSTR)m_Path;
  29. m_pDir = opendir(m_ImageDir); //获取该路径下的第一个文件
  30. for (int i = 0; i < 2; i ++) //过滤目录 .. 和 .
  31. {
  32. m_pEnt = readdir(m_pDir);

  这里图像的批量读取采用SHBrowseForFolder方法,在之前的博文中详细介绍过,网上也有很多资料,这里不再赘述。

  2.3 相关变量初始化

  由于要计算正确率,因此需要使用到一些局部临时变量,这里先对其进行初始化,至于每个变量的具体用途,在接下来的代码中会帮助大家理解:

  1. float sum_num_man = 0;
  2. float sum_num_women = 0;
  3. float current_num_man = 0;
  4. float current_num_women = 0;

  2.4 方法测试

  接下来开始循环读取测试文件夹下的测试样本,进行性别识别、累加计数、计算正确率,先给出代码:

  1. /**********方法测试**********/
  2. while (m_pDir && (m_pEnt = readdir(m_pDir)) != NULL)
  3. {
  4. //首先判断当前文件是否为图像文件
  5. char* pJpg = strstr(m_pEnt->d_name,".jpg");
  6. char* pBmp = strstr(m_pEnt->d_name,".bmp");
  7. char* pPng = strstr(m_pEnt->d_name,".png");
  8. char* pJPG = strstr(m_pEnt->d_name,".JPG");
  9. if(pJpg==NULL && pBmp==NULL && pPng==NULL && pJPG==NULL)
  10. {
  11. break;
  12. }
  13. //拼出文件的全路径
  14. sprintf(imageFullName,"%s%s",m_ImageDir,m_pEnt->d_name);
  15. IplImage* src;
  16. CvvImage srcCvvImg;
  17. //加载图像
  18. src = cvLoadImage(imageFullName);
  19. detect_and_draw(src);
  20. //根据标签来判断当前图片是男性还是女性
  21. char* pMan = strstr(m_pEnt->d_name,"man");
  22. char* pWomen = strstr(m_pEnt->d_name,"women");
  23. if (pMan != NULL)//如果当前图片为男性
  24. {
  25. sum_num_man = sum_num_man + 1;
  26. }
  27. if (pWomen != NULL)//如果当前图片为女性
  28. {
  29. sum_num_women = sum_num_women + 1;
  30. }
  31. if (m_genderLabel == 1)//如果当前图片计算机检测为男性
  32. {
  33. current_num_man = current_num_man + 1;
  34. }
  35. if (m_genderLabel == 2)//如果当前图片计算机检测为女性
  36. {
  37. current_num_women = current_num_women + 1;
  38. }
  39. cvReleaseImage(&src);
  40. }

  这里主要有一下几个问题需要强调:

  (1)测试样本集的制作。在制作测试样本集的时候,就用到了图像批量改名的手段,男性测试样本的名称用“man”来标记,女性测试样本的名称用“women”来进行标记,效果如下:

589850-20160229170849689-1980269877.png

589850-20160229170942955-2068658949.png

  (2)正确率计算方法。在这里计算识别率的方法非常简单,首先通过strstr()函数判断当前测试样本的名称中是含有“man”还是含有“women”,若含有“man”字符串,则说明当前测试样本为男性测试样本,计数器sum_num_man加一,同理若测试样本为女性样本时则女性计数器sum_num_women加一。 然后在根据性别识别结果(标签m_genderLabel )来对当前的识别情况进行计数。

  2.5、计算识别率并显示

  遍历完成后,开始计算识别率并将其显示在编辑框中,代码如下:

  1. /**********计算识别结果并显示**********/
  2. char ch[10];
  3. if (sum_num_man > sum_num_women)//如果当前测试集为男性
  4. {
  5. float result1;
  6. result1 = current_num_man / sum_num_man;
  7. int res1;
  8. res1 = result1 * 100;
  9. itoa(res1,ch,10);
  10. GetDlgItem(IDC_Effect_Man)->SetWindowTextA(ch);
  11. }
  12. else
  13. {
  14. float result2;
  15. result2 = current_num_women / sum_num_women;
  16. int res2;
  17. res2 = result2 * 100;
  18. itoa(res2,ch,10);
  19. GetDlgItem(IDC_Effect_Wom)->SetWindowTextA(ch);
  20. }

  OK,此时F5调试运行程序,选择分类方法,初始化,选择测试样本文件夹,程序开始自动进行方法验证,得出识别率。

  三 注意事项

  1、批量读取文件的重要性

  从这两篇博文中可以说明文件遍历的重要性,C++乃至OpenCv都有相关的图像遍历方法,这里给出的SHBrowseForFolder方法只是其中之一,其中OpenCv也提供了Directory类,大家可以多参考网上资料。

  2、存在人脸检测失败的可能性

  这里有一个隐含的问题需要注意,在进行人脸检测时是存在检测失败的风险的,如果检测失败,虽然程序不会报错,但会导致识别率的误降低(具体原因大家自己思考哈),解决方案有三种,一是提前进行一遍人脸检测,将检测失败的图像剔除出去;二是设置人脸检测标志位,当人脸检测失败时不进行样本数目的累加;三是直接使用已经分割好的人脸做仿真,绕过人脸检测这一步,具体采用哪种方法改进大家自行决定吧。

如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢。

20180420194450188

发表评论

表情:
评论列表 (有 0 条评论,249人围观)

还没有评论,来说两句吧...

相关阅读