话说如今小喵也要起初写诗歌了,结果也便是那篇博客体育365网址

小喵的唠叨话:话说近些日子小喵也要从头写杂谈了,想了两周依旧未有头绪,不明了该写些什么。恰好又被分配了有些标记数据的干活,于是乎想写点代码,休闲一下。结果也等于那篇博客。对了,小喵对GUI编制程序一无所知,只知道Windows有MFC,Mac上的不精通。。。恰好听别人讲过QT,何况知道那么些分界面库是跨平台的,也就选择了这几个工具了。

叁个简练残忍的人脸认证表明工具的兑现,人脸标记

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

 

正文系原创,转发请注明出处~

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

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

 

那正是说以后初步和小喵一齐瞎猫似的捯饬QT吧~

先看一眼效果图:

是否乍一看还挺炫丽。功效上也幸好,至少轻易的标明专门的学业都能不辱职责了。那么让大家来一步一步的完成那一个工具吧。

 

一、效能需求

本条顺序首要的效劳是到位一位脸认证的标明工具。

具体来讲,就是给定非常多对面部的图纸,要标明一下这一对是还是不是同壹个人。同期,每部分的图形的人脸一张是生活照,一张是证件本,供给相同的时候标记出哪张是证照,那张是生活照。照片都以透过检查测量试验和对齐的,这么些工具只须求做到轻便的显示、评释、保存记录的专门的职业就足以。

自然思虑到一时候供给标记的list恐怕十分大,能够投入跳转的效应。标记结果都保存在内部存款和储蓄器,客户能够随时变动,点击保存,则写入硬盘。

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

二、数据结构

那么是否当今就能够入手写代码了呢?当然不是!

小喵写这些软件一共用了3天的小运,第一天成功了二个超简单demo程序,熟稔了须臾间QT的轩然大波增多,路线选取和展现图片的多少个效果与利益。之后又细致入微的合计了一晃各类数码的结构,才下手做了这一版工具。未有一个清楚的数码的定义,会促成好些个的无用功。所以,我们在写程序的时候,要在备选阶段多花一点时光来探讨,终究写代码才是最简便的事体不是吧?

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

三、分界面制作

GUI程序的分界面平昔是个很令人发烧的主题材料,记得在本科学习Java的时候,需求团结手写一个控件,使用new
JButton()类似的措施成立按键,然后增多到主分界面上,地方怎么的都得调用那个指标来设置,十二分的累赘。那么QT能否简化这些历程吧?答案是迟早的。

创造项目->选用Application->Qt Widgets
Application。然后项目名改成Anno
Pro,别的一切暗中认可设置,就创办好了二个品种了。那些起首的门类里面有3个公文夹:头文件,源文件和分界面文件,以及一个.pro结尾的品种布局文件。

既然如此供给编写制定界面,大家本来会想查看一下分界面文件了,双击MainWindow.ui(作者那边全都以默许的名字)。出现的是多少个充满各样控件的可视化分界面编辑器。

依据大家事先的分界面样式,拖动侧边包车型地铁控件,就足以成功分界面包车型客车编纂了。小喵这里只用到了两种控件:

QPushButton:各样按键

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

QFrame:三个器皿,小喵用它只是为着协会上更清楚

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

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

拖动达成后,双击空间,就可以给空间设置文本,同不平日间注意给各类控件起叁个称心满意的名字(起名字很要紧的!《代码大全》中居然用一章,好几十页的字数介绍怎么样命名)。

关于别的的控件,大家可以自动钻研。反正小喵现在的道行应该才是筑基。

那正是说大家就喜滋滋的做到了分界面包车型客车编制了~点击左右下的运转Logo(三角形的可怜),就可以看看本身的周转程序了!

博客原著:http://www.miaoerduo.com/qt/贰个粗略暴虐的人脸认证标记工具的完结.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的系列,用来代表申明结果的连串。尽管独有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的函数(也正是槽函数)。

大家得以给每七个内需增添事件的函数都用这种措施来绑定事件,最后头文件中会出现这么的扬言(函数名称的平整是: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 }

那么将来开班和小喵一齐瞎猫似的捯饬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一个简单粗暴的人脸认证标注工具的实现,人脸标注
小喵的唠叨话:话说近些日子小喵也要起来写杂文了,想了两周仍旧不曾眉目,不晓得该写…

先看一眼效果图:

体育365网址 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结尾的花色配置文件。

体育365网址 2

既然如此必要编写制定分界面,大家当然会想查看一下分界面文件了,双击MainWindow.ui(作者这里全是默许的名字)。出现的是一个洋溢种种控件的可视化分界面编辑器。

体育365网址 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的函数(也等于槽函数)。

体育365网址 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文件了。

体育365网址 5

自个儿那边给这几个文件取名字为image。

事后,提议在档案的次序的根目录里面新建二个文本夹,用来寄存财富。小喵的构造是那几个样子的:

体育365网址 6

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

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

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

体育365网址 7

以往,我们就足以在先后中央直属机关接访问这么些能源了。那也正是我们在此之前的那三个离奇的路线的由来了。

七、发布

时下,相信每三个喵粉的前后相继都能在融洽的微型Computer上欢娱的十七日游了。这么有趣的主次,怎么分享给其余人呢?

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

体育365网址 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

体育365网址 9

转发请表明出处~

相关文章