一个略粗暴的食指脸认证标注工具的落实个人档案

一个略粗暴的食指脸认证标注工具的落实个人档案

小喵的唠叨话:话说最近小喵也如起勾画随笔了,想了片两全或不曾头绪,不知道该写些什么。恰好又于分配了一些标注数据的办事,于是乎想写点代码,休闲一下。结果为便是就篇博客。对了,小喵对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控件点击右键就足以创立了

拖动完成后,双击空间,就能够给空间设置文本,同时注意给每个控件起一个如意的名字(起名字至极重大之!《代码大全》中居然用平等章节,好几十页的篇幅介绍怎么样命名)。

个人档案,关于其他的控件,我们好活动钻研。反正小喵现在底道行应该才是筑基。

这我们便愉快的做到了界面的编排了~点击左右生之运行图标(三角形的那么些),就可以看到温馨之周转程序了!

季、数据定义及起首化

我们往日曾经分析了俺们得之数量了,这有的起首利用代码的概念那么些协会。

开拓大家/*唯一的*/头文件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怎么长文件资源为?

每当品种视图下,右键项目->添加新文件->Qt->Qt Resource
File。就足以创立一个qrc文件了。

个人档案 5

我这边叫这文件取名为image。

日后,提出于路的干净目录内新建一个文本夹,用来存放在资源。小喵的结构是者样子的:

个人档案 6

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

从此回来QT,我们正好建好的image.qrc文件->Open in Editor。

优先上加前缀,这里描绘上/File。之后点击新建的/File目录,再点击添加->添加文(加文(Gavin))件,接纳我们的素材文件。最后之效用图如下:

个人档案 7

随后,大家便得当程序中一向看那一个资源了。这也不怕是大家前面的那六只奇怪之途径的出于来了。

七、发布

眼前,相信各一个喵粉的次第都可以当和谐的统计机上愉快的游乐了。这么有意思的次,怎么分享给其外人也?

以及Windows上常用之VS类似,Qt Creator的左下角有只发表选项:

个人档案 8

慎选Release,然后构建整个项目就可了。之后找到大家的先后,双击就足以运作。

这你会合满面春风的将此顺序关你的好伙伴,得到的影响得是:那是吗!我自从不上马!

为啥吧?

虽然Qt是一个跨平台的界面库,但如若对方的处理器上从不装Qt,那么就是未可以运作。不过不用失落,Qt中已被闹一个美妙之解决办法。

小喵的统计机是Mac的,所以找到的解决方案吧是Mac的,Windows和Linux上为时有爆发接近的方,我们可自行检索。

http://www.cnblogs.com/E7868A/archive/2012/12/02/2798225.html

参照上述博客,我们以macdeployqt这多少个家伙来拍卖一下release的先后即便搞定。这时候若会意识本100k的主次成了22M。不过一向发放别人的时节,是足以向来运行的!

 

由来,本次的博客为止了。

整的项目在github上得以下载:

https://github.com/miaoerduo/Anno_pro

 

设你看本文对而来帮助,这请小喵喝杯茶吧O(∩_∩)O

个人档案 9

转载请表明出处~

admin

网站地图xml地图