Qt-OpenCV学习笔记--人脸识别

news2024/11/27 7:39:26

前言

本人从事机械设计12年,业余时间自学编程。

2022年4月6日,开始学习C#,

2022年9月7日,开始学习c++和Qt,

2022年10月28日,开始学习OpenCV,

今天终于搞定了传说中的 人脸识别 ,在此,做个记录。

人脸检测,是基于Haar特征的cascade分类器,

人脸识别,是基于LDA理论的Fisherface算法。

话不多说,上视频!(CSDN上传的视频,太清晰!)

人脸识别测试程序

测试代码

FaceRecognition.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    sm.cpp \
    widget.cpp

HEADERS += \
    sm.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

unix|win32: LIBS += -L$$PWD/../../../../../opencv/install/x64/mingw/lib/ -llibopencv_world460.dll

INCLUDEPATH += $$PWD/../../../../../opencv/install/include
DEPENDPATH += $$PWD/../../../../../opencv/install/include

sm.h

#ifndef SM_H
#define SM_H

#include <iostream>
#include "opencv2/core.hpp"



class sm
{
public:
    sm();

    //读取文件
    static void read_csv(const std::string& filename, std::vector<cv::Mat>& images, std::vector<int>& labels, char separator);

    //图像预处理:检测人脸、裁剪、缩放、保存、生成列表
    static void pretreatment(std::vector<cv::Mat> images, std::vector<int> labels,std::string path,int width,int height);

};

#endif // SM_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_7_clicked();

    void on_pushButton_2_clicked();

    void on_pushButton_3_clicked();

    void on_pushButton_5_clicked();

    void on_pushButton_6_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

sm.cpp

#include "sm.h"

//引用依赖
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <fstream>
#include <sstream>

#include <QDebug>

sm::sm()
{

}

void sm::pretreatment(std::vector<cv::Mat> images, std::vector<int> labels, std::string path, int width, int height)
{
    cv::Mat dst_shear;
    cv::Mat dst_resize;

    //创建级联分类器
    cv::CascadeClassifier cascade;
    //载入Haar特征分类器
    cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");
    //创建矩形容器
    std::vector<cv::Rect> rects;
    //遍历
    int flag=1;
    for(uint i=0;i<images.size();i++)
    {
        //人脸检测
        cascade.detectMultiScale(images[i],rects);
        //裁剪
        dst_shear = images[i](rects[0]).clone();
        //缩放
        resize(dst_shear,dst_resize,cv::Size(width,height),0,0,cv::INTER_AREA);
        //保存路径拼接
        std::string im_path1 = std::to_string(labels[i]);
        std::string im_path2 = std::to_string(flag);
        flag++;
        if(labels[i+1]!=labels[i])
        {
            flag=1;
        }
        std::string im_path3 = ".png";
        std::string im_path = path+im_path1+"-"+im_path2+im_path3;

        //调试
        QString qstr_im = QString::fromStdString(im_path);
        qDebug()<<qstr_im;

        //保存
        imwrite(im_path,dst_resize);
        //显示
        //std::string str = std::to_string(i);
        //imshow(str,dst_resize);
        //生成列表文件
        std::string list_name = "list.txt";
        std::string list_path = path + list_name;

        //调试
        QString qstr_list = QString::fromStdString(list_path);
        qDebug()<<qstr_list;

        std::ofstream list(list_path, std::ios::app);
        if (list.fail())
        {
            qDebug()<<"list文件打开失败,请检查文件路径!";
        }
        else
        {
            list<<im_path;
            list<<";";
            list<<im_path1;
            list<<"\n";
        }
    }
}

void sm::read_csv(const std::string &filename, std::vector<cv::Mat> &images, std::vector<int> &labels, char separator)
{
    //以只读方式读取文件
    std::ifstream file(filename, std::ios::in);
    if (!file)
    {
        qDebug()<<"文件打开失败,请检查文件路径!";        
    }
    else
    {
        //逐行读取文本,分离路径和标签
        std::string line, path, classlabel;
        //逐行读取
        while (getline(file, line))
        {
            //将读取到的文本转为字符串流
            std::stringstream stream(line);
            //分离路径
            getline(stream, path, separator);
            //分离标签
            getline(stream, classlabel);
            //若分离成功,则按照路径载入图像,设置标签
            if(!path.empty() && !classlabel.empty())
            {
                images.push_back(cv::imread(path,0));
                labels.push_back(atoi(classlabel.c_str()));
            }
        }
    }
}

widget.cpp

#include "widget.h"
#include "ui_widget.h"

//引用
#include "sm.h"

#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

#include <iostream>
#include <fstream>
#include <sstream>

#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <qdatetime.h>

//进行人脸识别的路径
QString face_path;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

//选择文件
void Widget::on_pushButton_clicked()
{
    QString filename = QFileDialog::getOpenFileName(this,"请选择列表文件",".","*.txt");
    if(!filename.isEmpty())
        {
            ui->lineEdit->setText(filename);

        }
}

//选择保存目录
void Widget::on_pushButton_7_clicked()
{
    QString dir = QFileDialog::getExistingDirectory(this,"请选择保存目录",".");
    if(!dir.isEmpty())
        {
            QString str = dir + "/";
            ui->lineEdit_3->setText(str);

        }
}

//训练模型
void Widget::on_pushButton_2_clicked()
{
    //获取文件路径
    std::string src_filename = ui->lineEdit->text().toStdString();
    if(src_filename.empty())
    {
        QMessageBox::warning(this,"警告","图像载入失败,请检查文件路径!");
        return;
    }
    //图像集合
    std::vector<cv::Mat> src_images;
    //标签集合
    std::vector<int> src_labels;
    //加载文件
    sm::read_csv(src_filename,src_images,src_labels,';');
    //判断读取是否成功
    if(src_images.size()<=1||src_labels.size()<=1)
    {        
        QMessageBox::warning(this,"警告","数据量不足,请检查数据列表!");
        return;
    }

    //调试
    qDebug()<<src_images.size();
    qDebug()<<src_labels.size();

    //获取图像保存路径
    std::string dst_path = ui->lineEdit_3->text().toStdString();
    if(dst_path.empty())
    {
        QMessageBox::warning(this,"警告","请检查文件保存路径!");
        return;
    }
    //图像预处理,生成新文件
    sm::pretreatment(src_images,src_labels,dst_path,100,100);

    //获取新文件路径
    std::string dst_filename = dst_path+"list.txt";
    //新图像集合
    std::vector<cv::Mat> dst_images;
    //新标签集合
    std::vector<int> dst_labels;
    //重新加载文件
    sm::read_csv(dst_filename,dst_images,dst_labels,';');

    // 创建模型
    cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
    // 训练模型
    model->train(dst_images, dst_labels);
    //保存模型
    model->write(dst_path+"model.xml");

    //提示
    QMessageBox::information(this,"消息","模型训练完成!");

}

//选择模型路径
void Widget::on_pushButton_3_clicked()
{
    QString filename = QFileDialog::getOpenFileName(this,"请选择模型",".","*.xml");
    if(!filename.isEmpty())
        {
            ui->lineEdit_2->setText(filename);

        }
}

//选择需要识别的图像,缩放,保持比例,显示
void Widget::on_pushButton_5_clicked()
{
    face_path = QFileDialog::getOpenFileName(this,"选择一个图片",".","*.jpg *.png *.bmp");
    if(!face_path.isEmpty())
    {
        //加载图像
        QPixmap* pix= new QPixmap;
        pix->load(face_path);
        //图像缩放
        QPixmap* npix= new QPixmap;
        *npix = pix->scaled(ui->label_4->size(),Qt::KeepAspectRatio);
        //显示
        ui->label_4->setPixmap(*npix);

    }

}

//人脸识别
void Widget::on_pushButton_6_clicked()
{
    cv::Mat src,
            dst_shear,
            dst_resize;


    //创建级联分类器
    cv::CascadeClassifier cascade;
    //载入Haar特征分类器
    cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");

    //加载图像
    if(face_path.isEmpty())
    {
        QMessageBox::warning(this,"警告","请先选择一个图像!");
        return;
    }
    else
    {
        src = cv::imread(face_path.QString::toStdString(),0);
    }

    //创建矩形容器
    std::vector<cv::Rect> rects;
    //识别人脸
    cascade.detectMultiScale(src,rects);

    //裁剪图像
    dst_shear = src(rects[0]).clone();

    //缩放
    cv::resize(dst_shear,dst_resize,cv::Size(100,100),0,0,cv::INTER_AREA);

    if(ui->lineEdit_2->text().isEmpty())
    {
        QMessageBox::warning(this,"警告","请检查模型加载路径!");
    }
    else
    {
        // 创建模型
        cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
        //载入训练好的模型
        model->read(ui->lineEdit_2->text().QString::toStdString());

        //进行识别
        int predictedLabel;
        double confidence;
        model->predict(dst_resize,predictedLabel,confidence);

        //打印结果
        QDateTime cur = QDateTime::currentDateTime();

        QString str;
        switch (predictedLabel)
        {
            case 1:
                str = "周敏慧";
                break;
            case 2:
                str = "林志玲";
                break;
            case 3:
                str = "黄渤";
                break;
            case 4:
                str = "单大伟";
                break;
            default:
                str = "这个人我不认识!";
        }

        ui->textBrowser->append(cur.toString("yyyy-MM-dd hh:mm:ss"));
        ui->textBrowser->append(str);
    }
}

widget.ui

测试结果

        综上,将导入的图像进行裁剪和缩放,仅保存人脸部分,用于训练模型;然后加载训练好的模型,进行人脸识别,最后将识别的信息予以显示。

        代码经过修改,可以用于 门禁系统 或者 人脸打卡

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67998.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spirngcloud的基本介绍与服务注册

1. 应用系统架构的演变 单应用架构 -> 应用服务器和数据库服务器分离 -> 应用服务器集群 -> 数据库压力变大&#xff0c;数据库读写分离 -> 引入缓存机制缓解数据库的压力 -> 数据库的水平/垂直拆分(数据库分库分表) -> 应用的拆分&#xff08;微服务&…

[附源码]计算机毕业设计绿色生活交流社区网站Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

GitLab搭建

以docker方式运行gitlab docker run --detach \--hostname gitlab.mczaiyun.top \--publish 8443:443 --publish 8090:80 --publish 8022:22 \--name gitlab \--restart always \--volume /root/gitlab/config:/etc/gitlab \--volume /root/gitlab/logs:/var/log/gitlab \--vo…

Elasticsearch入门(二)基本操作(索引、文档、映射)

数据格式 Elasticsearch 是面向文档型数据库&#xff0c;一条数据在这里就是一个文档。为了方便大家理解&#xff0c;我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比ES 里的 Index 可以看做一个库&#xff0c;而 Types 相当于表&#x…

【在Vue脚手架项目中使用qs框架】

目录 1. 安装qs框架 2. 在main.js中添加配置 1. 安装qs框架 在前端项目中&#xff0c;可以使用qs框架&#xff0c;实现“将对象转换为FormData格式的数据”。 首先&#xff0c;安装此框架&#xff1a; 如果没有权限进入C盘找到cmd的执行软件&#xff0c;用管理员启动&…

练习题(12-06)

目录 1.最小数 2.数天数 3.非常特殊的数 4.最大值路径 5.拆分质数 6.文件拷贝 7.除去重复单词 8.变成回文字符串 1.最小数 题目描述 请找到一个大于2022的最小数&#xff0c;这个最小的数转换成二进制后&#xff0c;最低的6个二进制全为0. 思路&#xff1a;枚举即…

[附源码]Python计算机毕业设计Django物业管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Java开发:反射机制

一、Java Reflection Reflection&#xff08;反射&#xff09;是java被视为动态语言的关键&#xff08;Java是静态语言&#xff0c;因为有了反射所以又被成为“准动态语言”&#xff09; 二、重点&#xff1a;一个类只有一个Class对象 三、反射的优缺点 优点&#xff1a;可…

【JAVA问题解决方案】02.Freemarker导出Excel超出上限分表解决方案

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;Java问题解决方案&#xff08;都是一些常见的问题解决方案&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下有关Freemarker导出Excel分表的解决方案&#xff0c;超级详细。&a…

Tomcat7+ 弱口令 后台getshell漏洞

Tomcat7 弱口令 && 后台getshell漏洞 &#x1f349; shell 此环境来自vulhub工程&#xff1a; https://github.com/vulhub/vulhub 以下测试环境为ubuntu 20.04 &#x1f349;目录Tomcat7 弱口令 && 后台getshell漏洞环境准备弱密码登录war文件上传蚁剑连接…

SoftEther linux与windows使用

1、SoftEther简介 我们来先科普一下什么是SoftEther吧&#xff0c;SoftEther是由日本筑波大学的登大遊在硕士论文中提出的开源、跨平台、多重协议的虚拟专用网方案,其实我更看重的是他的内网穿透功能&#xff0c;下面来一张图片&#xff0c;说明它的强大。 SoftEther正确定的安…

【力扣】剑指offer第二天

剑指offer第二天[剑指 Offer 06. 从尾到头打印链表 - 力扣&#xff08;LeetCode&#xff09;]方法一代码方法二代码[剑指 Offer 24. 反转链表 - 力扣&#xff08;LeetCode&#xff09;]方法一代码方法二代码[剑指 Offer 35. 复杂链表的复制 - 力扣&#xff08;LeetCode&#xf…

日期功能组件(加上左右点击,更改月份)

要求效果&#xff1a; &#xff0c;直接可以显示年月&#xff0c;并且点击左右箭头可以更改月份 点击这个日期&#xff0c;可以选择。 最后实现效果&#xff1a; 实现&#xff1a; 先找一个插件&#xff0c;可以选择时间 <!-- 选择年月--><div class"form-…

SAP ARFCSTATE ARFCSDATA TRFCQOUT

SELECT count(1) FROM arfcsstate --18848155 20221122 清理前 -- 6582457 清理后 SELECT count(1) FROM ARFCSDATA a -- 81732430 202221122 清理前 -- 25064628 清理后 SELECT count(1) FROM TRFCQOUT t --18848095 202221122 …

面试必备:HashMap底层源码原来是这么简单(分析)

微信公众号&#xff1a;SteveCode 关注可了解更多的编程及开发经验。问题或建议&#xff0c;请公众号留言; 专注Java技术干货分享&#xff0c;Java技术、数据结构、相关工具、Spring全家桶、intellij idea…… 如果你觉得对你有帮助&#xff0c;欢迎赞赏 transient 发现了这个关…

re:Invent现场访谈:云计算如何重塑智能制造

众所周知&#xff0c;近年来包括中国和美国在内&#xff0c;世界上各个主要国家对制造业的关注度都在不断提升。也正因为如此&#xff0c;在2022亚马逊云科技re:Invent全球大会上&#xff0c;有关云计算技术在智能制造行业的应用&#xff0c;也理所当然地成为了诸多与会嘉宾关注…

【JMeter】线程组jp@gc - Stepping Thread Group(逐步线程组)

jpgc - Stepping Thread Group 逐步线程组 Action to be taken after a Sample error 发生sample错误时 Continue 继续 Start Next Thread Loop 开始新的线程 Stop Thread 停止线程 Stop Test 停止测试 Stop Test Now 立即停止测试 Threads Scheduling Parameters 线程调度…

STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试

一、前言 在STM32项目开发中&#xff0c;经常会用到存储芯片存储数据。 比如&#xff1a;关机时保存机器运行过程中的状态数据&#xff0c;上电再从存储芯片里读取数据恢复&#xff1b;在存储芯片里也会存放很多资源文件。比如&#xff0c;开机音乐&#xff0c;界面上的菜单图…

使用SpringBoot快速构建Web API

Dubbo 框架现在在国内的中小企业当中已经成为 Java 生态下服务化的事实标准&#xff0c;出现这种状态的原因很多&#xff0c;比如 Dubbo 框架设计优秀、文档和资料丰富、配置灵活、特性丰富等&#xff0c;但最主要的&#xff0c;我认为是 Java 开发人员对速度这一因素的痴迷。 …

详解设计模式:解释器模式

解释器模式&#xff08;interpreter pattern&#xff09;&#xff0c;是在 GoF 23 种设计模式中定义了的行为型模式。 解释器模式 这种模式被用在 SQL 解析、符号处理引擎等。 解释器模式 给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xf…