小喵的博客,不亮堂该写些什么

七、发布

脚下,相信每一个喵粉的次序都能在融洽的微处理器上欢乐的玩乐了。这么有意思的程序,怎么分享给其旁人呢?

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

图片 1

接纳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

图片 2

转载请注脚出处~

五,添加事变响应

小喵在此以前了解到,QT使用的是一种信号和槽的轩然大波机制,是一种非凡高等的体制。那么有没有怎么着简单的章程,为我们的每个控件绑定自己的的轩然大波呢?

在界面编辑界面下,右击亟需丰裕事件的上空,然后拔取转到槽。这时候会有为数不少增选,这里直接接纳clicked就足以。然后您会意识我们的mainwindow类中,多了一个pivate
slot的函数(也就是槽函数)。

大家可以给每一个亟待添加事件的函数都用这种艺术来绑定事件,最后头文件中会出现这样的扬言(函数名称的平整是: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 = ":File/images/unsure.png";
 2 const std::string YES_FILE = ":File/images/yes.gif";
 3 const std::string NO_FILE = ":File/images/no.gif";
 4 const std::string UNKNOWN_FILE = ":File/images/unknown.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 }

四、数据定义与开首化

咱们原先已经分析了大家需要的数目了,这一部分起先拔取代码的概念这些构造。

开拓我们/*唯一的*/头文件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。

四、数据定义与最先化

我们以前已经分析了大家需要的数据了,这有的开首利用代码的定义这一个协会。

开拓我们/*唯一的*/头文件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。

一、功用要求

以此程序重要的功力是完成一个人脸认证的标注工具。

具体来说,就是给定很多对脸部的图样,要标注一下这一对是不是同一个人。同时,每部分的图形的人脸一张是生活照,一张是证件照,需要同时标注出哪张是证件照,那张是生活照。照片都是透过检测和对齐的,这多少个工具只需要完成简单的显示、标注、保存记录的劳作就足以。

当然考虑到有时候需要标注的list可能很大,可以参与跳转的效益。标注结果都保存在内存,用户可以每一日变动,点击保存,则写入硬盘。

那么现在开端和小喵一起瞎猫似的捯饬QT吧~

这就是说现在伊始和小喵一起瞎猫似的捯饬QT吧~

六、添加资源

是因为大家的次序是急需publish出去的,因而图片文件等资源,必须带有在先后中。那么Qt怎么添加文(Gavin)件资源呢?

在项目视图下,右键项目->添加新文件->Qt->Qt Resource
File。就可以成立一个qrc文件了。

自身那边给那个文件取名为image。

未来,提出在类型的根目录里面新建一个文书夹,用来存放在资源。小喵的协会是以此样子的:

小喵的门类根目录新建了一个文本夹images,并将图像资料放入了这个文件夹。

从此未来回到QT,

http://www.bkjia.com/cjjc/1184032.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1184032.htmlTechArticle一个简单粗暴的人脸认证标注工具的实现,人脸标注
小喵的唠叨话:话说目前小喵也要从头写杂文了,想了两周仍旧没有头绪,不清楚该写…

一、效率需求

这些程序重要的职能是形成一个人脸认证的标号工具。

具体来说,就是给定很多对面部的图片,要标注一下这一对是不是同一个人。同时,每部分的图样的人脸一张是生活照,一张是证件照,需要同时标注出哪张是证件照,这张是生活照。照片都是因而检测和对齐的,这些工具只需要形成简单的显得、标注、保存记录的干活就可以。

自然考虑到有时候需要标注的list可能很大,可以进入跳转的功力。标注结果都保存在内存,用户可以随时变动,点击保存,则写入硬盘。

三、界面制作

GUI程序的界面一直是个很令人胃疼的问题,记得在本科学习Java的时候,需要协调手写一个控件,使用new
JButton()类似的措施开创按钮,然后添加到主界面上,地方怎么的都得调用这多少个目标来安装,非常的麻烦。那么QT能不可能简化这个进程吧?答案是迟早的。

创建项目->采取Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他任何默认设置,就创立好了一个项目了。这么些起头的门类里面有3个文本夹:头文件,源文件和界面文件,以及一个.pro结尾的品种布局文件。

图片 3

既然需要编制界面,大家自然会想查看一下界面文件了,双击MainWindow.ui(我这里全体都是默认的名字)。出现的是一个满载各类控件的可视化界面编辑器。

图片 4

依照大家事先的界面样式,拖动右边的控件,就足以完成界面的编辑了。小喵这里只用到了三种控件:

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。

三、界面制作

GUI程序的界面平素是个很令人感冒的题材,记得在本科学习Java的时候,需要团结手写一个控件,使用new
JButton()类似的章程创建按钮,然后添加到主界面上,地方怎么的都得调用那些目标来安装,非常的繁琐。那么QT能不能简化这么些过程吧?答案是肯定的。

创设项目->采用Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他所有默认设置,就成立好了一个类型了。这一个先导的品类里面有3个文件夹:头文件,源文件和界面文件,以及一个.pro结尾的门类配置文件。

图片 5

既然如此需要编制界面,我们本来会想查看一下界面文件了,双击MainWindow.ui(我这里全部都是默认的名字)。出现的是一个洋溢各样控件的可视化界面编辑器。

图片 6

依照大家在此之前的界面样式,拖动左侧的控件,就可以形成界面的编制了。小喵这里只用到了三种控件:

QPushButton:各样按钮

QLabel:所以显得文字和图像的区域都是这这多少个控件

QFrame:一个容器,小喵用它只是为着协会上更清晰

QSlider:滑动条,小喵用的是水平滑动条

QStatusBar:状态栏,
这应当是自带的,要是删掉的话,在MainWindow控件点击右键就可以创设了

拖动完成后,双击空间,就可以给空间设置文本,同时注意给每个控件起一个满足的名字(起名字很重要的!《代码大全》中如故用一章,好几十页的字数介绍怎么样命名)。

至于此外的控件,我们可以自动钻研。反正小喵现在的道行应该才是筑基。

这就是说大家就兴冲冲的完成了界面的编制了~点击左右下的运作图标(三角形的相当),就足以见到自己的运转程序了!

一、效能要求

本条顺序主要的机能是完成一个人脸认证的标注工具。

具体来说,就是给定很多对面部的图片,要标注一下这一对是不是同一个人。同时,每部分的图形的人脸一张是生活照,一张是证件照,需要同时标注出哪张是证件照,这张是生活照。照片都是因此检测和对齐的,这个工具只需要完成简单的突显、标注、保存记录的做事就足以。

当然考虑到有时候需要标注的list可能很大,能够进入跳转的功效。标注结果都保存在内存,用户可以每日变动,点击保存,则写入硬盘。

一个简便粗暴的人脸认证标注工具的落实,人脸标注

小喵的唠叨话:话说近来小喵也要从头写杂谈了,想了两周依旧不曾头脑,不晓得该写些什么。恰好又被分配了一些标号数据的办事,于是乎想写点代码,休闲一下。结果也就是这篇博客。对了,小喵对GUI编程一窍不通,只晓得Windows有MFC,Mac上的不知底。。。恰好听说过QT,而且知道这些界面库是跨平台的,也就采用了那么些工具了。

 

本文系原创,转载请声明出处~

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

博客原文:http://www.miaoerduo.com/qt/一个简单粗暴的人脸认证标注工具的实现.html

 

那么现在伊始和小喵一起瞎猫似的捯饬QT吧~

先看一眼效果图:

是不是乍一看还挺炫酷。效能上也还好,至少简单的标号工作都能做到了。那么让大家来一步一步的做到这几个工具吧。

七、发布

眼前,相信每一个喵粉的程序都能在温馨的微处理器上欢乐的游乐了。这么有意思的顺序,怎么分享给其外人呢?

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

图片 7

分选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

图片 8

转载请注解出处~

五,添加风波响应

小喵在此之前理解到,QT使用的是一种信号和槽的事件机制,是一种相当高档的编制。那么有没有哪些简单的法子,为我们的每个控件绑定自己的的事件呢?

在界面编辑界面下,右击亟待丰裕事件的长空,然后拔取转到槽。这时候会有过多抉择,这里一直采纳clicked就可以。然后你会意识我们的mainwindow类中,多了一个pivate
slot的函数(也就是槽函数)。

图片 9

我们得以给每一个索要加上事件的函数都用这种形式来绑定事件,最后头文件中会出现这样的宣示(函数名称的平整是: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 }

三、界面制作

GUI程序的界面一贯是个很令人高烧的题材,记得在本科学习Java的时候,需要团结手写一个控件,使用new
JButton()类似的主意开创按钮,然后添加到主界面上,地点怎么的都得调用这些目的来安装,分外的累赘。那么QT能不能够简化这么些历程吧?答案是必然的。

开创项目->拔取Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其他一切默认设置,就创办好了一个品类了。那个开始的花色里面有3个文本夹:头文件,源文件和界面文件,以及一个.pro结尾的体系布局文件。

既然需要编制界面,我们自然会想查看一下界面文件了,双击MainWindow.ui(我那里全体都是默认的名字)。出现的是一个满载各个控件的可视化界面编辑器。

按照我们前边的界面样式,拖动左侧的控件,就可以完成界面的编撰了。小喵这里只用到了两种控件:

QPushButton:各类按钮

QLabel:所以显得文字和图像的区域都是这这多少个控件

QFrame:一个容器,小喵用它只是为了社团上更清楚

QSlider:滑动条,小喵用的是水平滑动条

QStatusBar:状态栏,
这应当是自带的,如若删掉的话,在MainWindow控件点击右键就可以创造了

拖动完成后,双击空间,就可以给空间设置文本,同时注意给每个控件起一个顺心的名字(起名字很重点的!《代码大全》中居然用一章,好几十页的字数介绍如何命名)。

关于其他的控件,我们可以活动钻研。反正小喵现在的道行应该才是筑基。

那么我们就欣然的成就了界面的编撰了~点击左右下的运转图标(三角形的相当),就足以见到自己的运作程序了!

 

图片 10

二、数据结构

那么是不是现在就可以出手写代码了啊?当然不是!

小喵写这一个软件一共用了3天的年月,第一天成功了一个超简单demo程序,熟识了一下QT的轩然大波添加,路径拔取和出示图片的多少个功用。之后又密切的思索了刹那间各个数码的组织,才出手做了这一版工具。没有一个清晰的数据的定义,会导致广大的无用功。所以,我们在写程序的时候,要在备选阶段多花一点日子来合计,毕竟写代码才是最简易的事务不是吧?

小喵的唠叨话:话说近期小喵也要从头写杂谈了,想了两周如故没有头绪,不知道该写些什么。恰好又被分配了一些标号数据的行事,于是乎想写点代码,休闲一下。结果也就是这篇博客。对了,小喵对GUI编程一窍不通,只了解Windows有MFC,Mac上的不知情。。。恰好听说过QT,而且知道这些界面库是跨平台的,也就采取了那个工具了。

小喵的唠叨话:话说近日小喵也要从头写杂文了,想了两周依旧没有头绪,不精通该写些什么。恰好又被分配了几许标号数据的干活,于是乎想写点代码,休闲一下。结果也就是这篇博客。对了,小喵对GUI编程一窍不通,只理解Windows有MFC,Mac上的不亮堂。。。恰好听说过QT,而且知道那一个界面库是跨平台的,也就选择了那多少个工具了。

二、数据结构

那么是不是先天就可以动手写代码了啊?当然不是!

小喵写这些软件一共用了3天的时光,第一天成功了一个超简单demo程序,熟谙了一下QT的轩然大波添加,路径接纳和呈现图片的多少个效能。之后又密切的想念了弹指间各样数码的社团,才下手做了这一版工具。没有一个分明的数目标定义,会导致过多的无用功。所以,我们在写程序的时候,要在备选阶段多花一点时间来研究,毕竟写代码才是最简单易行的事体不是啊?

  1. 输入数据格式:因为小喵的做事条件下,我们都对linux有局部询问,所以可以自行生成好图片的门路的list,这里统一要求,list必须是偶数行(2n行),代表n对,相邻的图样为一对。
  2. 标明数据存储:考虑到大家不但需要标注是不是一对,还得标注哪张是证件照,所以不妨直接在读数据的时候就分为两份,这样就用四个std::vector<std::string>来囤积就行了。
  3. 标明过程的景色:我们需要理解标注过程中的这一个信息吗?首要应该有:总数据量,当前已标明的对数。
  4. 标明结果:每一对都有一组对应地
    结果,考虑到有4中状态:未标注,不确定,不配合,匹配这四种,大家定义一个枚举的场所表enum
    AnnoState就好。之后用一个std::vector<enum
    AnnoState>来囤积标注结果。

本文系原创,转载请阐明出处~

先看一眼效果图:

博客原文:http://www.miaoerduo.com/qt/一个简单粗暴的人脸认证标注工具的兑现.html

是不是乍一看还挺炫酷。效率上也还好,至少简单的标号工作都能成就了。那么让我们来一步一步的成功那些工具吧。

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

五,添加事件响应

小喵在此以前精通到,QT使用的是一种信号和槽的风波机制,是一种特别高档的编制。那么有没有咋样简单的主意,为我们的各类控件绑定自己的的风波呢?

在界面编辑界面下,右击内需添加事件的空中,然后采取转到槽。这时候会有成千上万抉择,这里间接选用clicked就可以。然后您会发现我们的mainwindow类中,多了一个pivate
slot的函数(也就是槽函数)。

图片 11

大家得以给每一个需要充裕事件的函数都用这种办法来绑定事件,最后头文件中会出现如此的讲明(函数名称的规则是: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 = ":File/images/unsure.png";
 2 const std::string YES_FILE = ":File/images/yes.gif";
 3 const std::string NO_FILE = ":File/images/no.gif";
 4 const std::string UNKNOWN_FILE = ":File/images/unknown.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 }

 

图片 12

是不是乍一看还挺炫酷。功用上也还好,至少简单的标注工作都能做到了。那么让我们来一步一步的做到这些工具吧。

六、添加资源

出于大家的程序是亟需publish出去的,因而图片文件等资源,必须带有在程序中。那么Qt怎么添加文(加文)件资源呢?

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

图片 13

自家这边给这一个文件取名为image。

随后,指出在品种的根目录里面新建一个文件夹,用来存放资源。小喵的结构是其一样子的:

图片 14

小喵的系列根目录新建了一个文书夹images,并将图像资料放入了这一个文件夹。

之后回到QT,我们刚建好的image.qrc文件->Open in Editor。

先添加前缀,这里写上/File。之后点击新建的/File目录,再点击添加->添加文(Gavin)件,拔取我们的材料文件。最后的意义图如下:

图片 15

其后,咱们就可以在程序中一向访问这个资源了。这也就是大家事先的这两个想不到的门径的由来了。

二、数据结构

那么是不是明日就可以出手写代码了啊?当然不是!

小喵写这多少个软件一共用了3天的岁月,第一天成功了一个超简单demo程序,熟谙了一下QT的事件添加,路径选拔和体现图片的多少个效用。之后又精心的思考了须臾间各类数码的布局,才入手做了这一版工具。没有一个清晰的数量的定义,会招致不少的无用功。所以,我们在写程序的时候,要在预备阶段多花一点时刻来研讨,毕竟写代码才是最简易的事体不是吗?

  1. 输入数据格式:因为小喵的行事环境下,我们都对linux有一部分叩问,所以可以自行生成好图片的途径的list,这里统一要求,list必须是偶数行(2n行),代表n对,相邻的图片为一对。
  2. 标明数据存储:考虑到我们不光需要标注是不是部分,还得标注哪张是证件照,所以不妨间接在读数据的时候就分为两份,这样就用多个std::vector<std::string>来储存就行了。
  3. 标明过程的景色:大家需要领悟标注过程中的这多少个信息吗?紧要应该有:总数据量,当前已标明的对数。
  4. 标明结果:每一对都有一组对应地
    结果,考虑到有4中状态:未标注,不确定,不般配,匹配这四种,我们定义一个枚举的场地表enum
    AnnoState就好。之后用一个std::vector<enum
    AnnoState>来储存标注结果。

本文系原创,转载请表明出处~

先看一眼效果图:

博客原文:http://www.miaoerduo.com/qt/一个简约粗暴的人脸认证标注工具的贯彻.html

 

 

六、添加资源

出于我们的次序是索要publish出去的,因而图片文件等资源,必须含有在程序中。那么Qt怎么添加文(加文)件资源呢?

在档次视图下,右键项目->添加新文件->Qt->Qt Resource
File。就足以创制一个qrc文件了。

图片 16

本身这边给那些文件取名为image。

尔后,提出在档次的根目录里面新建一个文书夹,用来存放资源。小喵的构造是这一个样子的:

图片 17

小喵的品类根目录新建了一个文件夹images,并将图像资料放入了这些文件夹。

然后再次来到QT,大家刚建好的image.qrc文件->Open in Editor。

先添加前缀,那里写上/File。之后点击新建的/File目录,再点击添加->添加文(加文)书,采用我们的资料文件。最后的效率图如下:

图片 18

从此将来,我们就足以在先后中一贯访问这么些资源了。这也就是大家前面的这四个出人意料的不二法门的由来了。

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

相关文章