QT+Opencv实现人脸检测与性别识别(1)

本文利用seetaface进行人脸检测,opencv的dnn模块进行性别识别,借助QT创建用户界面,详细介绍了从数据准备、人脸检测到性别识别的实现过程,涉及LFWD性别标签添加、人脸区域截取等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

seetaface开源人脸检测框架实现人脸检测,opencv+dnn模块实现性别分类,qt做显示界面,完成一个课程设计。

依赖库:opencv3.1+ 包含dnn模块,QT5

1.性别分类网络训练

1.1.训练数据准备

下载lfw人脸数据库,由于原始数据集未提供性别标签,参考了GitHub项目LFWgender,访问一个网络api接口,根据姓名对数据添加上标签。最终获得了约1600张图片作为训练数据,600张图片作为验证集,男女样本各一半。

1.2.人脸检测与脸部区域截取

性别识别的第一步是人脸检测,而我们送到卷积网络训练和预测的数据,实际上就是检测出来的人脸区域,因此,我们首先要对lfw数据集做人脸检测与脸部截取,截取前与截取后的图如下

截取前这里写图片描述截取后

我们用seetaface开源人脸检测做人脸检测,检测出的人脸区域为红色框部分。为了让输入到卷积网络的图片包含充分的信息,我们把人脸区域按照一定方法,扩展到一个更合理的区域,即绿色框区域。实际的训练集与验证集都应该是最右边的图片,即截取之后的图片。

实现步骤

1.用Python脚本获取图片文件访问路径
形如
E:\female\Adelina_Avila_0001.jpg;0
E:\female\Adelina_Avila_0001.jpg;1
这样的txt文件,前面是文件绝对路径,后面是标签,0表示女性。

#encoding:utf-8
import os
'''
获取male样本和female样本中的图片的绝对路径,
并保存在txt文件中,male样本路径后加上标签1,female样本路径后加上标签2,
如
male0.jpg;1
male1.jpg;1
female0.jpg;2
female1.jpg;2
'''
#root = os.getcwd()
#print root
male_path = os.path.abspath('female') #male相对路径
file_list = os.listdir(male_path)#female路径下的图片相对路径 male0.jpg
#print file_list
f = open('label.txt','w')
abs_path_list=[]#图片绝对路径
print 'male......'
for file in file_list:
    file_name = male_path+'\\'+file
    print file_name
    abs_path_list.append(file_name+';0\n')

female_path = os.path.abspath('valfemale')
file_list = os.listdir(female_path)
print 'female......'
for file in file_list:
    file_name = female_path+'\\'+file
    abs_path_list.append(file_name+';1\n')

f.writelines(abs_path_list)

2.用seetaface人脸检测提取绿色框部分并保存

遍历之前Python脚本获取的图片访问路径,逐个检测人脸,人脸区域扩展,依据label 0 1保存到femaleroi和maleroi文件夹

#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>

#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
//#include<opencv2/opencv.hpp>

#include "face_detection.h"

using namespace std;
using namespace cv;
void read_csv(string& fileName, vector<string>& images, vector<int>& labels, char separator = ';')//参考opencv官网的一段例子
{
    ifstream file(fileName.c_str(), ifstream::in);
    string line, path, label;
    while (getline(file, line))
    {
        stringstream lines(line);
        getline(lines, path, separator);
        getline(lines, label);
        if (!path.empty() && !label.empty())
        {
            images.push_back(path);
            labels.push_back(atoi(label.c_str()));
        }
    }
}
void SplitString(const std::string& s, std::vector<std::string>& v, const std::string& c)
//为了从E:/female/Adelina_Avila_0001.jpg截取Adelina_Avila_0001.jpg字符段写的字符串函数
{
    std::string::size_type pos1, pos2;
    pos2 = s.find(c);
    pos1 = 0;
    while (std::string::npos != pos2)
    {
        v.push_back(s.substr(pos1, pos2 - pos1));

        pos1 = pos2 + c.size();
        pos2 = s.find(c, pos1);
    }
    if (pos1 != s.length())
        v.push_back(s.substr(pos1));
}

int main()
{
    seeta::FaceDetection detector("seeta_fd_frontal_v1.0.bin");

    detector.SetMinFaceSize(40);
    detector.SetScoreThresh(2.f);
    detector.SetImagePyramidScaleFactor(0.8f);
    detector.SetWindowStep(4, 4);
    vector<string> images;
    vector<int> labels;
    string csv_path = "E:/label.txt";
    read_csv(csv_path, images, labels);
    for (int i = 0; i < labels.size();++i)
    {
        string img_path = images[i];
        int label = labels[i];
        cv::Mat img = cv::imread(img_path, cv::IMREAD_UNCHANGED);
        cv::Mat img_gray;

        if (img.channels() != 1)
            cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
        else
            img_gray = img;

        seeta::ImageData img_data;
        img_data.data = img_gray.data;
        img_data.width = img_gray.cols;
        img_data.height = img_gray.rows;
        img_data.num_channels = 1;

        std::vector<seeta::FaceInfo> faces = detector.Detect(img_data);
#ifdef USE_OPENMP
        cout << "OpenMP is used." << endl;
#else
        cout << "OpenMP is not used. " << endl;
#endif

#ifdef USE_SSE
        cout << "SSE is used." << endl;
#else
        cout << "SSE is not used." << endl;
#endif
        cv::Rect face_rect;
        int32_t num_face = static_cast<int32_t>(faces.size());
        cout << "人脸个数" << num_face << endl;
        for (int32_t i = 0; i < num_face; i++) {
            face_rect.x = faces[i].bbox.x;
            face_rect.y = faces[i].bbox.y;
            face_rect.width = faces[i].bbox.width;
            face_rect.height = faces[i].bbox.height;
            //扩充脸部至更大范围的头部,250是原图片大小
            float extend = MIN(MIN(face_rect.x, face_rect.y), MIN(250 - 1 - face_rect.x - face_rect.width, 250 - 1 - face_rect.y - face_rect.height));
            extend = MIN(extend, face_rect.height / 4);
            face_rect.x -= extend;
            face_rect.y -= extend;
            face_rect.width = face_rect.width + 2 * extend;
            face_rect.height = face_rect.height + 2 * extend;
            cout << "人脸大小" << face_rect.size() << endl;
            if ((face_rect.x + face_rect.width)>img.rows || (face_rect.y + face_rect.height) > img.cols ||(face_rect.x<0) ||(face_rect.y<0))
                continue;
            cv::Mat roi;
            img(face_rect).copyTo(roi);
            string maleroot = "E:/valmale/";
            string femaleroot = "E:/valfemale/";
            vector<string> vs;
            string c = "/";
            SplitString(img_path, vs, c);//截取图片文件名,以便存储roi图片
            if (label == 0)
                cv::imwrite(maleroot + vs[vs.size() - 1], roi);
                //vs vector(E:,female,Adelina_Avila_0001.jpg),所以vs.size()-1指向最后的元素
            else
                cv::imwrite(femaleroot + vs[vs.size() - 1], roi);
            //cv::rectangle(img, face_rect, CV_RGB(0, 0, 255), 4, 8, 0);

        }

    }

    return 0;
}

上述Python与cpp代码完成了将检测到的人脸扩展到一定范围,并将此范围截取保存到对应文件夹的操作。训练集和验证集可以自己切分,最后会有MaleTrainRoi, FemaleTrainRoi, MaleValRoi, FemaleValRoi四个文件夹,为后续使用caffe训练自己的数据做准备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值