小喵的博客,小喵的博客澳门皇冠官网app

小喵的唠叨话:话说近日小喵也要初叶写故事集了,想了两周依旧不曾头脑,不清楚该写些什么。恰好又被分配了少数标注数据的工作,于是乎想写点代码,休闲一下。结果也正是那篇博客。对了,小喵对GUI编制程序一窍不通,只晓得Windows有MFC,Mac上的不亮堂。。。恰好听他们讲过QT,而且知道这么些界面库是跨平台的,也就选取了那个工具了。

1个不难凶横的人脸认证标注工具的落到实处,人脸标注

小喵的唠叨话:话说方今小喵也要起来写故事集了,想了两周依然尚未眉目,不驾驭该写些什么。恰好又被分配了某些标明数据的干活,于是乎想写点代码,休闲一下。结果也便是这篇博客。对了,小喵对GUI编制程序一无所知,只领悟Windows有MFC,Mac上的不明了。。。恰好听他们讲过QT,而且知道这一个界面库是跨平台的,也就选拔了这些工具了。

 

本文系原创,转发请申明出处~

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

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

 

那么将来始发和小喵一起瞎猫似的捯饬QT吧~

先看一眼效果图:

是还是不是乍一看还挺炫酷。成效上也幸好,至少不难的标注工作都能到位了。那么让大家来一步一步的到位这么些工具吧。

 

一 、功用必要

那些程序首要的效率是成功一人脸认证的标注工具。

具体来说,正是给定很多对脸部的图形,要标注一下这一对是或不是同一人。同时,每部分的图样的人脸一张是生活照,一张是证件照,要求同时标注出哪张是证件照,那张是生活照。照片都以通过检查和测试和对齐的,那个工具只须求做到简单的显得、标注、保存记录的工作就能够。

理所当然考虑到有时候须求标注的list恐怕极大,能够加入跳转的效果。标注结果都保存在内部存储器,用户能够随时变动,点击保存,则写入硬盘。

本文系原创,转发请评释出处~

二 、数据结构

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

小喵写那么些软件一共用了3天的小运,第2天成功了三个超不难demo程序,熟识了一晃QT的轩然大波添加,路径选取和展现图片的几个成效。之后又精心的沉思了弹指间种种数据的布局,才出手做了这一版工具。没有贰个清晰的多少的定义,会招致广大的无用功。所以,我们在写程序的时候,要在备选阶段多花一点日子来思考,毕竟写代码才是最简易的作业不是啊?

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

叁 、界面制作

GUI程序的界面一向是个很令人头疼的标题,记得在本科学习Java的时候,需求自个儿手写3个控件,使用new
JButton()类似的艺术开创按钮,然后添加到主界面上,地点怎么的都得调用这么些指标来安装,10分的麻烦。那么QT能还是不能简化这么些进度吧?答案是一定的。

创设项目->接纳Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其余一切暗许设置,就创办好了叁个品种了。这些开头的门类里面有2个公文夹:头文件,源文件和界面文件,以及1个.pro结尾的品种布局文件。

既然如此需求编写制定界面,大家本来会想查看一下界面文件了,双击MainWindow.ui(作者那里全部都以暗中同意的名字)。出现的是3个洋溢各样控件的可视化界面编辑器。

按照大家在此之前的界面样式,拖动右侧的控件,就能够形成界面包车型地铁编排了。小喵那里只用到了二种控件:

QPushButton:各个按钮

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

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

QSlider:滑动条,小喵用的是程度滑动条

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

拖动完成后,双击空间,就足以给空间设置文本,同时注意给各类控件起三个兴高采烈的名字(起名字很重庆大学的!《代码大全》中竟然用一章,好几十页的字数介绍怎样命名)。

有关此外的控件,大家能够自行钻研。反正小喵未来的道行应该才是筑基。

那就是说我们就欢悦的实现了界面的编排了~点击左右下的运作图标(三角形的百般),就足以观察自身的周转程序了!

博客原来的书文:http://www.miaoerduo.com/qt/3个简练残暴的人脸认证标注工具的兑现.html

④ 、数据定义与开端化

大家原先一度分析了作者们必要的多少了,这一部分最先选择代码的定义那些构造。

开辟大家/*唯一的*/头文件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的品种,用来代表标注结果的品种。尽管唯有四个状态,大家依旧足以一向约定多少个int值来表示,但相信本人,为这么七个状态定义2个枚举类型是一心有供给的。

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

下一步就是开头化了。初步化的进程当然得写在构造函数里,那里,小喵在开首化的时候强迫用户选取1个标注的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:那些组件是3个文书对话框,在这之中有八个卓殊有效的函数:getOpenFileName用于选择一个文件,并重返文件名;getSaveFileName用于选拔2个文书来保存数据,并回到二个文本名。那多个函数的参数很多,小喵只用到了前边的一个,用到的参数依次是:父组件,标题,起初目录。其余的参数的效劳,喵粉能够去官网查一下。

QMessage博克斯::information,这么些函数的法力是显得贰个新闻窗口。三个参数分别表示:父组件,标题,内容,按钮样式。

信任大家懂一丢丢C++的学问的话,很不难看懂那段代码。

此间就是运用了二个巡回,让用户选用文件,要是选取成功了,则读取数据到大家的list中,最后先导化了任何的参数,在调用display函数来呈现。这么些display函数是大家友好编写的,前面会说到。其它,assert函数是预知,他保管了断言的数额的合法性,假如不合规,程序会退出。想使用那一个函数,须要蕴含头文件assert.h。

 

澳门皇冠官网app,五,添加事件响应

小喵在此以前通晓到,QT使用的是一种信号和槽的轩然大波机制,是一种十二分尖端的机制。那么有没有哪些简单的艺术,为大家的每一个控件绑定自身的的轩然大波吧?

在界面编辑界面下,右击内需添加事件的空中,然后接纳转到槽。那时候会有不少抉择,这里一分区直属机关接公投择clicked就能够。然后你会发现大家的mainwindow类中,多了3个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 }

最先河大家定义了多少个图片的途径。那足以是相对路径恐怕相对路径。我们那边的门径设置的比较奇怪,在底下我们会讲到。

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 }

那么今后发轫和小喵一起瞎猫似的捯饬QT吧~

六 、添加能源

鉴于大家的顺序是亟需publish出去的,由此图片文件等财富,必须带有在先后中。那么Qt怎么添加文件财富呢?

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

自小编那边给那些文件取名为image。

事后,建议在品种的根目录里面新建贰个文本夹,用来存放财富。小喵的结构是这么些样子的:

小喵的类型根目录新建了八个文书夹images,并将图像资料放入了那个文件夹。

日后回来QT,

http://www.bkjia.com/cjjc/1184032.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1184032.htmlTechArticle一个简单粗暴的人脸认证标注工具的实现,人脸标注
小喵的唠叨话:话说近日小喵也要起来写随想了,想了两周依旧尚未眉目,不亮堂该写…

先看一眼效果图:

澳门皇冠官网app 1

是或不是乍一看还挺炫酷。成效上也幸亏,至少不难的标注工作都能成功了。那么让我们来一步一步的完结这些工具吧。

壹 、功用供给

其一顺序主要的成效是做到一人脸认证的标注工具。

具体来说,就是给定很多对面部的图形,要标注一下这一对是还是不是同1个人。同时,每部分的图片的人脸一张是生活照,一张是证件照,必要同时标注出哪张是证件照,那张是生活照。照片都是经过检测和对齐的,那一个工具只必要做到简单的来得、标注、保存记录的办事就足以。

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

贰 、数据结构

那么是否现在就足以入手写代码了呢?当然不是!

小喵写那一个软件一共用了3天的年月,第1天成功了三个超不难demo程序,纯熟了弹指间QT的轩然大波添加,路径选用和突显图片的多少个作用。之后又仔细的想想了一晃各个数据的构造,才入手做了这一版工具。没有1个鲜明的多寡的定义,会造成广大的无用功。所以,大家在写程序的时候,要在备选阶段多花一点年华来考虑,究竟写代码才是最简便易行的业务不是啊?

  1. 输入数据格式:因为小喵的工作条件下,我们都对linux有局地询问,所以能够自行生成好图片的路径的list,那里统一需求,list必须是偶数行(2n行),代表n对,相邻的图纸为一对。
  2. 标明数据存款和储蓄:考虑到大家不但要求标注是否有些,还得标注哪张是证件照,所以不妨间接在读数据的时候就分为两份,那样就用七个std::vector<std::string>来囤积就行了。
  3. 标明进度的情景:大家要求知道标注进程中的那多少个音信呢?主要应该有:总数据量,当前已标明的对数。
  4. 标明结果:每一对都有一组对应地
    结果,考虑到有4中状态:未标注,不分明,不般配,匹配这三种,大家定义三个枚举的意况表enum
    AnnoState就好。之后用多少个std::vector<enum
    AnnoState>来存款和储蓄标注结果。

三 、界面制作

GUI程序的界面一直是个很令人头痛的标题,记得在本科学习Java的时候,必要协调手写一个控件,使用new
JButton()类似的措施创建按钮,然后添加到主界面上,地方怎么的都得调用那几个指标来设置,11分的麻烦。那么QT能或无法简化那几个进度吧?答案是毫无疑问的。

制造项目->选用Application->Qt Widgets
Application。然后项目名改成Anno
Pro,其余一切暗中认可设置,就创办好了3个门类了。那么些早先的花色里面有3个文本夹:头文件,源文件和界面文件,以及多少个.pro结尾的体系布局文件。

澳门皇冠官网app 2

既是必要编制界面,大家本来会想查看一下界面文件了,双击MainWindow.ui(笔者这边全部都是私下认可的名字)。现身的是三个充斥种种控件的可视化界面编辑器。

澳门皇冠官网app 3

遵循我们后边的界面样式,拖动左侧的控件,就能够完毕界面包车型地铁编写制定了。小喵这里只用到了二种控件:

QPushButton:各类按钮

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

QFrame:3个容器,小喵用它只是为了组织上更明显

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

能够见到,小喵添加了2个enum的品种,用来代表标注结果的花色。即使唯有四个情景,大家甚至能够直接约定多少个int值来代表,但相信笔者,为这么5个状态定义二个枚举类型是全然有必不可少的。

以往大家富有的成员变量都以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:那个组件是2个文书对话框,在那之中有四个相当可行的函数:getOpenFileName用于选拔二个文件,并赶回文件名;getSaveFileName用于选用二个文本来保存数据,并重回贰个文件名。那多个函数的参数很多,小喵只用到了前面包车型客车2个,用到的参数依次是:父组件,题目,初叶目录。其余的参数的服从,喵粉能够去官网查一下。

QMessageBox::information,那些函数的机能是显得一个音讯窗口。多个参数分别表示:父组件,标题,内容,按钮样式。

深信大家懂一小点C++的知识的话,很不难看懂那段代码。

那边便是应用了贰个循环往复,让用户挑选文件,借使选拔成功了,则读取数据到大家的list中,最后早先化了此外的参数,在调用display函数来显示。这几个display函数是我们温馨编排的,前边会说到。其余,assert函数是预感,他保险了断言的数量的合法性,借使违规,程序会脱离。想选取这些函数,必要包罗头文件assert.h。

五,添加风浪响应

小喵在此之前驾驭到,QT使用的是一种信号和槽的风云机制,是一种尤其高档的编写制定。那么有没有如何简单的方法,为大家的各类控件绑定本人的的风浪吧?

在界面编辑界面下,右击急需加上事件的长空,然后选拔转到槽。那时候会有诸多选择,那里直接选取clicked就足以。然后您会发觉我们的mainwindow类中,多了二个pivate
slot的函数(也正是槽函数)。

澳门皇冠官网app 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的保有的函数的概念。

第1办事是,给每一种事件编写修改数据的代码,而不去负权利何界面相关的一些。各样控件能够通过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 }

最开始大家定义了五个图片的路线。那足以是相对路径恐怕相对路径。大家那里的路子设置的相比奇怪,在底下大家会讲到。

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文件了。

澳门皇冠官网app 5

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

未来,建议在品种的根目录里面新建3个文件夹,用来存放在能源。小喵的结构是其一样子的:

澳门皇冠官网app 6

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

现在重返QT,大家刚建好的image.qrc文件->Open in 艾德itor。

先添加前缀,那里写上/File。之后点击新建的/File目录,再点击添加->添加文件,选用大家的资料文件。最后的功力图如下:

澳门皇冠官网app 7

然后,大家就足以在先后中平素访问那个财富了。那也正是大家事先的那八个出人意料的不二法门的由来了。

七、发布

当下,相信每多少个喵粉的次第都能在和谐的总括机上欢腾的玩耍了。这么有意思的顺序,怎么分享给别的人呢?

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

澳门皇冠官网app 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

澳门皇冠官网app 9

转发请注脚出处~

相关文章