快捷搜索:

一个简单粗暴的人脸认证标注工具的实现

来源:http://www.shanghai-sourcing.com 作者:金沙澳门官网55网址 人气:198 发布时间:2019-10-22
摘要:小喵的唠叨话:话说近来小喵也要起来写随想了,想了两周依然不曾眉目,不知晓该写些什么。恰好又被分配了好几标记数据的办事,于是乎想写点代码,休闲一下。结果也正是那篇博

小喵的唠叨话:话说近来小喵也要起来写随想了,想了两周依然不曾眉目,不知晓该写些什么。恰好又被分配了好几标记数据的办事,于是乎想写点代码,休闲一下。结果也正是那篇博客。对了,小喵对GUI编制程序胸无点墨,只晓得Windows有MFC,Mac上的不知道。。。恰好据悉过QT,并且知道这几个分界面库是跨平台的,也就接纳了那些工具了。

 

本文系原创,转发请注脚出处~

小喵的博客:http://www.miaoerduo.com

博客原著:http://www.miaoerduo.com/qt/三个简约粗暴的人脸认证申明工具的达成.html

 

那么今后始于和小喵一齐瞎猫似的捯饬QT吧~

先看蒸蒸日上眼效果图:

图片 1

是还是不是乍后生可畏看还挺炫人眼目。成效上也幸而,起码轻易的标明专业都能一挥而就了。那么让咱们来一步一步的成就这几个工具吧。

生机勃勃、成效要求

其生气勃勃顺序首要的意义是成功一人脸认证的注脚工具。

一个简单粗暴的人脸认证标注工具的实现。具体来讲,正是给定相当多对面部的图形,要注脚一下那新闯祸物正在生机勃勃对是或不是同一位。同一时间,每部分的图片的人脸一张是生活照,一张是证照,须求同一时候标明出哪张是证件本,那张是生活照。照片都是通过检查测验和对齐的,这些工具只要求形成轻松的来得、标记、保存记录的行事就足以。

当然思索到不经常候必要声明的list或然异常的大,可以出席跳转的功效。标记结果都保存在内部存款和储蓄器,客户能够随即变动,点击保存,则写入硬盘。

二、数据结构

那么是还是不是明日就足以动手写代码了呢?当然不是!

小喵写那么些软件意气风发共用了3天的小时,第一天成功了二个超不难demo程序,熟稔了须臾间QT的平地风波增加,路线选择和显示图片的多少个效果与利益。之后又留心的探究了意气风发晃各样数据的构造,才入手做了那旭日初升版工具。未有三个显明的多寡的定义,会变成广大的无用功。所以,大家在写程序的时候,要在备选阶段多花一点时间来思考,毕竟写代码才是最简便易行的业务不是吧?

  1. 一个简单粗暴的人脸认证标注工具的实现。输入数据格式:因为小喵的干活条件下,大家都对linux有风华正茂部分打听,所以可以自行生成好图片的门路的list,这里统后生可畏供给,list必得是偶数行(2n行),代表n对,相邻的图样为生机勃勃对。
  2. 一个简单粗暴的人脸认证标注工具的实现。标注数据存款和储蓄:思虑到大家不但供给标记是或不是如日中天对,还得标明哪张是证照,所以不要紧直接在读数据的时候就分为两份,那样就用四个std::vector<std::string>来积累就行了。
  3. 标记进程的图景:我们必要通晓标明进程中的那个新闻吗?重要应该有:总量据量,当前已评释的对数。
  4. 注脚结果:每蒸蒸日上对都有大器晚成组对应地 结果,思量到有4中状态:未表明,不鲜明,不相称,相配那各种,大家定义一个枚举的场地表enum AnnoState就好。之后用二个std::vector<enum AnnoState>来囤积标明结果。

三、分界面制作

GUI程序的界面一直是个很令人咳嗽的标题,记得在本科学习Java的时候,必要自个儿手写叁个控件,使用new JButton()类似的方法创立按键,然后增添到主分界面上,地点怎么的都得调用那几个指标来设置,十三分的烦琐。那么QT能否简化这几个进度吧?答案是早晚的。

创制项目->接受Application->Qt Widgets Application。然后项目名改成Anno Pro,别的全部暗中同意设置,就创办好了三个门类了。那一个最先的品种里面有3个文件夹:头文件,源文件和分界面文件,以至贰个.pro结尾的花色陈设文件。

图片 2

既然必要编写制定分界面,大家当然会想查看一下分界面文件了,双击MainWindow.ui(小编那边全是私下认可的名字)。出现的是三个充斥各种控件的可视化分界面编辑器。

图片 3

根据我们事先的分界面样式,拖动左侧的控件,就足以做到分界面包车型大巴编排了。小喵这里只用到了两种控件:

QPushButton:各个开关

QLabel:所以显得文字和图像的区域都以那这几个控件

QFrame:贰个容器,小喵用它只是为着组织上更鲜明

一个简单粗暴的人脸认证标注工具的实现。QSlider:滑动条,小喵用的是程度滑动条

QStatusBar:状态栏, 那应该是自带的,借使删掉的话,在MainWindow控件点击右键就能够创制了

拖动达成后,双击空间,就足以给空间设置文本,同一时候注意给各种控件起叁个开心慰勉的名字(起名字很要紧的!《代码大全》中竟然用后生可畏章,好几十页的字数介绍怎样命名)。

有关其余的控件,我们能够自行钻研。反正小喵将来的道行应该才是筑基。

那么我们就欣然的实现了界面包车型大巴编写了~点击左右下的运转Logo(三角形的非常),就足以看见自身的运作程序了!

四、数据定义与早先化

作者们原先曾经解析了大家须要的数量了,这部分起初利用代码的概念那一个协会。

张开我们/*唯一的*/头文件mianwindow.h,增添须求的变量,小喵就平昔把温馨的头文件复制下来了:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 #include <vector>
 6 #include <string>
 7 namespace Ui {
 8 
 9 class MainWindow;
10 }
11 
12 class MainWindow : public QMainWindow
13 {
14     enum AnnoState {
15         UNKNOWN = 0,  // 未标注
16         YES = 1,      // 匹配
17         NO = 2,       // 不匹配
18         UNSURE = 3    // 不确定
19     };
20 
21 public:
22     explicit MainWindow(QWidget *parent = 0);
23     ~MainWindow();
24 
25 private:
26     Ui::MainWindow *ui; // 自带的,ui界面的接口
27     std::vector<std::string> image_list_1;  // 用来存放左边的图片的list
28     std::vector<std::string> image_list_2;  // 用来存放右边的图片的list
29     int current_idx;                        // 当前图片对的id
30     int total_pair_num;                     // 总共的图片对的数目
31     std::vector< AnnoState > annotation_list;  // 标注的结果
32 };
33 
34 #endif // MAINWINDOW_H

能够看看,小喵增加了二个enum的项目,用来表示标记结果的品种。即使独有4个情形,大家以至能够直接约定多少个int值来表示,但相信小编,为如此4个情景定义贰个枚举类型是一点一滴有供给的。

从此以后我们具有的分子变量都以private的。具体意思,注释中也可以有写明。

下一步正是初步化了。初阶化的历程当然得写在构造函数里,这里,小喵在带头化的时候强迫顾客选取二个标号的list,假设不那样做,会有众多的不测处境。请见谅小喵的怠惰。。。

 1 MainWindow::MainWindow(QWidget *parent) :
 2     QMainWindow(parent),
 3     ui(new Ui::MainWindow)
 4 {
 5     ui->setupUi(this);
 6 
 7     // 选择输入文件
 8     while (1) {
 9         QString file_name = QFileDialog::getOpenFileName(this, "choose a file to annotate", ".");
10         if (file_name.isEmpty()) {
11             int ok = QMessageBox::information(this, "choose a file to annotate", "Don't want to work now?", QMessageBox::Ok | QMessageBox::Cancel);
12             if (ok == QMessageBox::Ok) {
13                 exit(0);
14             }
15             continue;
16         }
17         std::ifstream is(file_name.toStdString());
18         std::string image_name;
19         bool is_odd = true;
20         while (is >> image_name) {
21             if (is_odd) {
22                 this->image_list_1.push_back(image_name);
23             } else {
24                 this->image_list_2.push_back(image_name);
25             }
26             is_odd = !is_odd;
27         }
28         is.close();
29 
30         if (image_list_1.size() != image_list_2.size()) {
31             QMessageBox::information(this, "choose a file to annotate", "this image list is not even", QMessageBox::Ok);
32             continue;
33         }
34         if (0 == image_list_1.size()) {
35             QMessageBox::information(this, "choose a file to annotate", "this image list is empty", QMessageBox::Ok);
36             continue;
37         }
38         break;
39     }
40 
41     assert(image_list_1.size() == image_list_2.size());
42     // 初始化其他参数
43     this->total_pair_num = image_list_1.size();
44     this->current_idx = 0;
45     std::vector<AnnoState> annotation_list(this->total_pair_num, AnnoState::UNKNOWN);
46     this->annotation_list.swap(annotation_list);
47 
48     display();
49 }

此处用了七个QT的机件:

QFileDialog:这一个组件是三个文书对话框,此中有五个极度管用的函数:getOpenFileName用于接纳多个文件,并再次回到文件名;getSaveFileName用于接受二个文书来保存数据,并赶回三个文本名。那五个函数的参数非常多,小喵只用到了后边的3个,用到的参数依次是:父组件,标题,起先目录。其余的参数的意义,喵粉能够去官方网址查一下。

QMessageBox::information,这些函数的功效是呈现贰个消息窗口。多少个参数分别表示:父组件,标题,内容,开关样式。

深信我们懂一丝丝C++的知识的话,十分轻松看懂这段代码。

此处就是使用了二个巡回,让客商选用文件,假如选用成功了,则读取数据到我们的list中,最后起头化了另外的参数,在调用display函数来显示。这几个display函数是大家本人编排的,前面会提及。别的,assert函数是预见,他保管了断言的数据的合法性,假设非法,程序会退出。想利用那些函数,必要包括头文件assert.h。

五,加多事变响应

小喵从前掌握到,QT使用的是大器晚成种实信号和槽的事件机制,是如火如荼种拾贰分尖端的体制。那么有没有啥轻易的不二秘诀,为我们的每一个控件绑定本人的的事件呢?

在分界面编辑分界面下,右击内需增多事件的半空中,然后采用转到槽。那时候会有广大抉择,这里直接接收clicked就能够。然后您会意识大家的mainwindow类中,多了三个pivate slot的函数(也正是槽函数)。

图片 4

笔者们能够给每叁个亟待加上事件的函数都用这种办法来绑定事件,最后头文件中会出现这样的宣示(函数名称的平整是:on_控件名_非时域信号类型):

1 private slots:
2     void on_pushButton_save_clicked();
3     void on_pushButton_ok_clicked();
4     void on_pushButton_no_clicked();
5     void on_pushButton_unsure_clicked();
6     void on_pushButton_next_clicked();
7     void on_pushButton_prev_clicked();
8     void on_pushButton_switch_clicked();
9     void on_horizontalSlider_progress_sliderReleased();

在源文件中,也会生成空的函数定义。我们只须求团结姣好函数定义就水到渠成!

下边给出的是除了save的兼具的函数的概念。

珍视办事是,给每种事件编写修改数据的代码,而不去承当任何分界面相关的大器晚成部分。各种控件可以透过this->ui来安装和获得。使用Qt Creator的时候,要丰硕利用智能提示。

 1 /**
 2  * @brief MainWindow::on_pushButton_ok_clicked
 3  * 标注为"匹配"
 4  */
 5 void MainWindow::on_pushButton_ok_clicked()
 6 {
 7     this->annotation_list[this->current_idx] = MainWindow::AnnoState::YES;
 8     ++ this->current_idx;
 9     display();
10 }
11 
12 /**
13  * @brief MainWindow::on_pushButton_no_clicked
14  * 标注为"不匹配"
15  */
16 void MainWindow::on_pushButton_no_clicked()
17 {
18     this->annotation_list[this->current_idx] = MainWindow::AnnoState::NO;
19     ++ this->current_idx;
20     display();
21 }
22 
23 /**
24  * @brief MainWindow::on_pushButton_unsure_clicked
25  * 标注为"不确定"
26  */
27 void MainWindow::on_pushButton_unsure_clicked()
28 {
29     this->annotation_list[this->current_idx] = MainWindow::AnnoState::UNSURE;
30     ++ this->current_idx;
31     display();
32 }
33 
34 /**
35  * @brief MainWindow::on_pushButton_next_clicked
36  * 移动到下一组
37  */
38 void MainWindow::on_pushButton_next_clicked()
39 {
40     ++ this->current_idx;
41     display();
42 }
43 
44 /**
45  * @brief MainWindow::on_pushButton_prev_clicked
46  * 移动到上一组
47  */
48 void MainWindow::on_pushButton_prev_clicked()
49 {
50     -- this->current_idx;
51     display();
52 }
53 
54 /**
55  * @brief MainWindow::on_pushButton_switch_clicked
56  * 交换两边的图片
57  */
58 void MainWindow::on_pushButton_switch_clicked()
59 {
60     std::string tmp = this->image_list_1[this->current_idx];
61     this->image_list_1[this->current_idx] = this->image_list_2[this->current_idx];
62     this->image_list_2[this->current_idx] = tmp;
63     display();
64 }
65 
66 /**
67  * @brief MainWindow::on_horizontalSlider_progress_sliderReleased
68  * 拖放进度条,控制进度
69  */
70 void MainWindow::on_horizontalSlider_progress_sliderReleased()
71 {
72     int pos = this->ui->horizontalSlider_progress->value();
73     this->current_idx = pos;
74     this->display();
75 }

时至前些天,我们的大致的效应逻辑就编写完了。

那正是说怎么让界面上出示我们的系统状态呢?注意到了小编们地点的每贰个函数都调用了display这一个函数了吧?那一个函数正式承受绘制分界面包车型客车功效。

某些首要介绍四个函数:

 1 const std::string UNSURE_FILE = ":Fileunsure.png";
 2 const std::string YES_FILE = ":Fileyes.gif";
 3 const std::string NO_FILE = ":Fileno.gif";
 4 const std::string UNKNOWN_FILE = ":Fileunknown.png";
 5 
 6 /**
 7  * @brief set_image 将图像设置到label上,图像自动根据label的大小来缩放
 8  * @param label
 9  * @param image
10  */
11 void set_image(QLabel *label, const QPixmap &image) {
12     float ratio(0.);
13     ratio = 1. * label->width() / image.width();
14     ratio = fmin( 1. * label->height() / image.height(), ratio );
15     QPixmap m = image.scaled(static_cast<int>(image.width() * ratio), static_cast<int>(image.height() * ratio));
16     label->setPixmap(m);
17 }
18 
19 void set_image(QLabel *label, const std::string image_path) {
20     QPixmap image(image_path.c_str());
21     set_image(label, image);
22 }
23 
24 /**
25  * @brief MainWindow::display n
26  * 根据系统中的所有的变量来设置当前界面中的各个部分的内容
27  */
28 void MainWindow::display() {
29 
30     if (this->current_idx >= this->total_pair_num) {
31         QMessageBox::information(this, "annotation over", "Congratulations! You've finished all the job! Please save your work :)", QMessageBox::Ok);
32         this->current_idx = this->total_pair_num - 1;
33     }
34     if (this->current_idx < 0) {
35         QMessageBox::information(this, "annotation warning", "You must start at 0 (not a negative position, I konw you wanna challenge this app) :)", QMessageBox::Ok);
36         this->current_idx = 0;
37     }
38 
39     // 进度条
40     this->ui->horizontalSlider_progress->setRange(0, this->total_pair_num - 1);
41     this->ui->horizontalSlider_progress->setValue(this->current_idx);
42 
43     // 状态栏
44     this->ui->statusBar->showMessage(QString((std::to_string(this->current_idx + 1) + " / " + std::to_string(this->total_pair_num)).c_str()));
45 
46     // 文件名
47     std::string image_name_1 = this->image_list_1[this->current_idx];
48     std::string image_base_name_1 = image_name_1.substr(image_name_1.find_last_of("/") + 1);
49     std::string image_name_2 = this->image_list_2[this->current_idx];
50     std::string image_base_name_2 = image_name_2.substr(image_name_2.find_last_of("/") + 1);
51     this->ui->label_image_name_1->setText(image_base_name_1.c_str());
52     this->ui->label_image_name_2->setText(image_base_name_2.c_str());
53 
54     // 显示图像
55     set_image(this->ui->label_image_view_1, image_name_1);
56     set_image(this->ui->label_image_view_2, image_name_2);
57 
58     // 显示标注结果
59     std::string show_image_name = UNKNOWN_FILE;
60     switch (this->annotation_list[this->current_idx]) {
61     case AnnoState::UNKNOWN:
62         show_image_name = UNKNOWN_FILE;
63         break;
64     case AnnoState::YES:
65         show_image_name = YES_FILE;
66         break;
67     case AnnoState::NO:
68         show_image_name = NO_FILE;
69         break;
70     case AnnoState::UNSURE:
71         show_image_name = UNSURE_FILE;
72         break;
73     }
74     set_image(this->ui->label_image_compare_status, show_image_name);
75 
76 }

最初前大家定义了4个图片的门径。这能够是相对路线只怕相对路径。我们那边的门路设置的可比奇异,在底下我们会讲到。

set_image担任将加以的图形绘制到QLabel上,为了展现的狼狈,图像会依据QLabel的尺寸来动态的缩放。那样就不会出现有个图像太大或太小的意况了。

display则是担负每个地区的绘图。

还差一步是保存结果:

 1 /*
 2  * @brief MainWindow::on_pushButton_save_clicked n
 3  * 保存结果文件
 4  */
 5 void MainWindow::on_pushButton_save_clicked()
 6 {
 7     QString file_name = QFileDialog::getSaveFileName(this, "choose a file to save", ".");
 8     if (file_name.isEmpty()) {
 9         QMessageBox::information(this, "choose a file to save", "please enter a legal file name", QMessageBox::Ok);
10         return;
11     }
12     std::ofstream os(file_name.toStdString());
13     for (int idx = 0; idx < static_cast<int>(this->annotation_list.size()); ++ idx) {
14         os << this->image_list_1[idx] << " " << this->image_list_2[idx] << " " << this->annotation_list[idx] << "n";
15     }
16     os.close();
17     QMessageBox::information(this, "save", "save result success", QMessageBox::Ok);
18 }

六、增添财富

鉴于大家的顺序是内需publish出去的,因而图片文件等财富,必需带有在前后相继中。那么Qt怎么添Gavin件能源呢?

在品种视图下,右键项目->增加新文件->Qt->Qt Resource File。就能够创造叁个qrc文件了。

图片 5

本身那边给这一个文件取名字为image。

从此以往,提出在等级次序的根目录里面新建一个文本夹,用来寄放能源。小喵的结构是其一样子的:

图片 6

小喵的花色根目录新建了几个文本夹images,并将图像资料放入了那一个文件夹。

而后回到QT,大家刚建好的image.qrc文件->Open in Editor。

先增加前缀,这里写上/File。之后点击新建的/File目录,再点击增加->添Gavin件,选择我们的素材文件。最终的效应图如下:

图片 7

然后,我们就足以在前后相继中一贯访谈这一个能源了。那也正是大家事先的那多少个离奇的路线的由来了。

七、发布

日前,相信每贰个喵粉的次第都能在和煦的Computer上高兴的游艺了。这么风趣的顺序,怎么分享给别的人呢?

和Windows上常用的VS类似,Qt Creator的左下角有个发表选项:

图片 8

慎选Release,然后创设整个项目就足以了。之后找到咱们的次序,双击就可以运营。

此刻你会欢欣的把那么些程序发给你的好同伴,得到的反馈自然是:这是啥!小编打不开!

为何吧?

纵然如此Qt是一个跨平台的分界面库,但万如日中天对方的微型计算机上尚未安装Qt,那么就不能够运作。可是并非衰颓,Qt中后生可畏度给出三个神奇的消除办法。

小喵的微处理器是Mac的,所以找到的消除方案也是Mac的,Windows和Linux上也可能有近似的办法,大家可以自动物检疫索。

参谋上述博客,大家利用macdeployqt这些工具来拍卖一下release的顺序就消除。那时候你会发觉原本100k的前后相继成为了22M。不过平昔发放旁人的时候,是足以一直运维的!

 

由来,此番的博客甘休了。

完整的品类在github上得以下载:

 

比如你以为本文对你有帮衬,那请小喵喝杯茶啊O(∩_∩)O

图片 9

转发请注解出处~

本文由金沙澳门官网55网址发布于金沙澳门官网55网址,转载请注明出处:一个简单粗暴的人脸认证标注工具的实现

关键词:

最火资讯