非刚性人脸识别 —— 实用工具

财物的阶层固化还是来空子打破的,可如果连认知能力都固定了,人顿时一世就着实没有梦想了。或许你少不是丰裕二替,但是若 经过努力有或成为富有一样代表,当然也来或要一般的中产。不过,如果你的并想都未改动,愿意一辈子凡是当下之样子,那立一世就真 的莫想了。

面向对象设计

  与人脸检测及人脸识别一样,人脸跟踪也是因为简单有的构成:数据以及算法。算法通过事先储存(即离线)的数额来训练模型,然后对新来的(即在线)数据实行某类操作。因此,采用面向对象设计是没错的挑选。

  以
opencv 2.x 版本被,可便宜引入
XML/YAML 文件存储类型,对算法来讲,会大大简化组织离线数据任务。下面通过一个假象类来展示是效应

  

  • 自从定义类
    foo

     1 // foo.h
     2 /*
     3     在下面的代码中,定义了一个序列化函数,可对 I/O 函数 read 和 write 实现序列化。
     4     FileStorage 类支持两种能被序列化的数据结构类型。
     5     为了简单起见,本章所有类将采用映射,其中每个用于存储的变量都会创建一个 FileNode::MAP 类型的 FileNode 对象。
     6     这需要分配给变量中的每个元素唯一键。为了保持一致性,将变量名作为标签
     7 */
     8 
     9 #include <opencv2/opencv.hpp>
    10 #include <iostream>
    11 using namespace cv;
    12 using namespace std;
    13 
    14 class foo {
    15 public:
    16     int a, b;        
    17     void write(FileStorage &fs) const {            // 序列化存储自定义数据类型
    18         assert(fs.isOpened());
    19         fs << "{" << "a" << a << "b" << b << "}";        // 创建 FileNode::MAP 类型的对象
    20     }
    21     void read(const FileNode& node) {            // 读取数据
    22         assert(node.type() == FileNode::MAP);
    23         node["a"] >> a;    node["b"] >> b;
    24     }
    25 };
    
  • 为了使
    FileStorage 类的序列化能正常干活,还需要定义write,
    read函数

     1 template<class T>
     2 void 
     3 write(FileStorage& fs, 
     4       const string&, 
     5       const T& x)
     6 {
     7   x.write(fs);
     8 }
     9 //==============================================================================
    10 template<class T>
    11 void 
    12 read(const FileNode& node, 
    13      T& x,
    14      const T& d)
    15 {
    16   if(node.empty())x = d; else x.read(node);
    17 }
    

     

 

  • 为吃保存与加载采用了序列化的用户从定义类易得容易,采用模块化函数定义了load_ft,save_ft函数

     1 template <class T> 
     2 T load_ft(const char* fname){
     3   T x; FileStorage f(fname,FileStorage::READ);
     4   f["ft object"] >> x; f.release(); return x;    // 定义与对象关联的标签都为 ft object
     5 }
     6 //==============================================================================
     7 template<class T>
     8 void save_ft(const char* fname,const T& x){
     9   FileStorage f(fname,FileStorage::WRITE);
    10   f << "ft object" << x; f.release();
    11 }
    
  • 用上述定义在
    ft.hpp 中
    图片 1图片 2

     1 /*
     2     ft.hpp
     3     用于加载、保存对象数据
     4 */
     5 
     6 #ifndef _FT_FT_HPP_
     7 #define _FT_FT_HPP_
     8 #include <opencv2/opencv.hpp> 
     9 //==============================================================================
    10 // 为了让保存和加载采用了序列化的用户自定义类变得容易,采用模块化函数定义了load_ft,save_ft函数
    11 template <class T> 
    12 T load_ft(const char* fname){
    13   T x; FileStorage f(fname,FileStorage::READ);
    14   f["ft object"] >> x; f.release(); return x;    // 定义与对象关联的标签都为 ft object
    15 }
    16 //==============================================================================
    17 template<class T>
    18 void save_ft(const char* fname,const T& x){
    19   FileStorage f(fname,FileStorage::WRITE);
    20   f << "ft object" << x; f.release();
    21 }
    22 //==============================================================================
    23 // 为了使 FileStorage 类的序列化能正常工作,还需要定义write, read函数
    24 template<class T>
    25 void 
    26 write(FileStorage& fs, 
    27       const string&, 
    28       const T& x)
    29 {
    30   x.write(fs);
    31 }
    32 //==============================================================================
    33 template<class T>
    34 void 
    35 read(const FileNode& node, 
    36      T& x,
    37      const T& d)
    38 {
    39   if(node.empty())x = d; else x.read(node);
    40 }
    41 //==============================================================================
    42 #endif
    

    ft.hpp

  • 主函数,有一个题材,储存到
    xml 文件连续报错,而 yaml 文件可以健康存取

     1 /*
     2     main.cpp
     3     测试 opencv 文件储存
     4 */
     5 
     6 #include "opencv_hotshots/ft/ft.hpp"
     7 #include "foo.h"
     8 
     9 int main() {
    10     foo A;                // 初始化自定义对象 A
    11     A.a = 1; A.b = 2;
    12     save_ft<foo>("foo.yaml", A);    // 将自定义对象存到 foo.yaml
    13     foo B = load_ft<foo>("foo.yaml");    // 读取对象
    14     cout << B.a << "," << B.b << endl;
    15 
    16     system("pause");
    17     return 0;
    18 }
    
  • 程序运行结果

       
        图片 3               
  图片 4

 

 

 

 布局商学

 01

于挣钱更麻烦之事

倘说国内富一时出吗苦恼,“如何致富”或许不是第一员之,他们真的的堵在于“如何近乎住财富”,特别是生时之接问题。

洋洋丁还针对“富二替代都是纨绔子弟”之类的传教津津乐道,觉得出钱人且是畜生,屁都不懂得,所以生的娃子呢都是有些兔崽子。对“富可三代”的现象心里别提有多爽。

然而实际,大部分大户对小教育的强调程度实在远超普通人,投入的资源更我们所无法想像的。

坐富人们都明白这道理:

财富的阶层固化还是发空子打破的,可若并认知能力还稳定了,人这一辈子就真正没有要了。

每当即时上头,富二代们其实面临比平常人大得多之挑战。因为他们一样出工作或就是得管住一个宏大之家族企业,尽管被的还是精英教育,想做好也并非易事。

用我们有时候为会见看这么的情报:

顿时首讲话的凡前海鑫钢铁集团有限公司董事长李海仓的男李兆会的涉:2003年年即22秋的李兆会担任集团董事长兼总经理,至2015年前旗鑫钢集团正式上破产流程。差不多10年时,李兆会“败光”百亿家产。

外界对李兆会的批评很多,比如有人说他迷恋炒股,最终致使企业倒闭。但后来有人统计,10年里其实他经过炒股赚了跨42只亿,前者根本属于无稽之道;也有人将问题对准他娶女明星,可从容豪娶明星的景象已经屡见不鲜。

李兆会的挫败,一方面是境内钢铁行业的萎靡导致,另一方面,则是营业如此大之一个集团本来就非常复杂的工作。他既老完美,只不过仓促上任(其大吃枪杀后他于爷爷指定为后任),少了自下而上的尽过程,这是其他其他作业都无法弥补的。

时国内第一批富起来的企业家纷纷上退休年龄,面临接班问题。但只要宏观考察,会意识能够做好的富二代表其实并无多,这种“败光家产”的政工并无鲜见。在即时上头,王思聪算做的正确的了,可为尚无直接对接王健林的次,而是挑了入投资行业。

稍许人对是可能会见看很爽:纵使你本复发生钱,迟早为会见当1-3代中败光,那我衷心也尽管平衡了。

他们从来不想掌握的是:

富有二替往往比我们受更好的傅,拥有强大无数倍之本钱、人脉和资源,可即使连这丛人思念只要成功都如此困难,更何况是普通人?

02

认知固化更麻烦打破

事业及一无所成的总人口不少且拿走出这样的见解:

友善败都是以时运不济、领导打压、才会埋没、起点太没有,而别人成功还是因生爸、有涉及、有资本、有人脉。

兴许你见面当我会说后面这些因素还无重大。不!这些要素本都太重要,可这些真正的就是能够培养成功么,不见得,那么基本上宽二代们的砸告诉了咱们是道理。

成功最为复杂了,从来不曾定式。即便你富有更多所谓能“造就成”的素,也不至于最后就能够得好的结果。

我是创业圈的,圈子里最为津津乐道的尽管是境内多可怜商店的“基因”问题。

准腾讯算牛逼了咔嚓?市值过万亿,布局之产业几乎遮住任何互联网。要钱富可敌国,要人才生世界最好顶尖的红颜,要术发生技艺,要资源有资源。按照我们的知,如此强硬的店铺,还免是怀念干啥都能学有所成?

不过腾讯在过去十年里频繁尝进入电商行业,结果尚且坏勿理想,最终大多单阳台倒闭的关闭,卖口之卖人。

再就是以阿里巴巴同等实力雄厚,从未尝试了当交际方面的卖力以及尝试,同样收效甚微。

博切磋企业的专家还在分析原因,最终于多人认同的故是企业是发“基因”的。企业之基因都了彼在原来领域的功成名就,但也会化为以新领域成功的绊脚石。

这就是说什么是基因吗?就是合作社看成完整的沉思模式,以及对世界、商业的认知能力。

腾飞至得等级后,不管对商家要私有来说,钱、人才、资源的题目都是可化解之,唯独思维模式与认知能力的更改跟升级没有定式可依照,需要加上时之升华才能够慢慢找到出路。

自即点以来,认知能力的定势其实是比财富阶层固化更吓人的事情。财富的一定总会以外力的震慑下逐渐自行破碎,可咀嚼能力的稳只能凭借自己节省修炼去打破。

假设你没此发现,这辈子就真的完蛋了。

03

财物和力的接力赛

前一阵子我转头了巡老家,发现之前就读的初中教育品质远不如自己读那会儿。当时每年还有一样好批判考上市里重点高中的学员,时不时还发能落得清华北大的地面人口,可这些年都几乎从来不了。

同一个老师拉,说起来呢是同样脸苦笑:

乡野师资收入低啊,所以只是凡有些力量并且青春的老师,现在几都走至珠三角教授去了,而且多数错过之还是私营学校、贵族学校。在老家一个月份将三四千,跑至外面一个月拿一万大多,这个选项做得毫不困难。

他说,现在还预留于老家的教职工,要么是年龄最好了,要么就算力量实际不行。在就批人教导下,学生的成就可想而知。所以,稍微富裕一点的阶层都不见面以孩子送及乡镇中学了,都当往县城、大城市送。

早先自己哉无掌握,为什么中国丁如此强调教育。北上广生的学区房早被炒上天价,动辄十几万竟几十万状元一同米;好的家庭教师现在时薪普遍过千,想要还要排队预约;夏令营等游学类收费啊是急性攀升,前阵子还有家长抱怨一个夏就是花费了好几万。

现今算是理解了,因为富人们已经发现:假如能力不配合的言辞,财富的继承是免深厚的。

我前面早已说过,人同食指里的斗并无是百米跑,而是接力赛。而今日当交叉的不但是财物了,还有力量。因为好之经济条件意味着又好的学校、老师、同学、学习材料,而这些要素积累到得水准,就意味着又强之汇总力量。

当屌丝们为嚣着读书无用论之上,富人们既以本质上化解问题了。这是同一项大可怕的事情,因为其代表普通人的火候真正越来越少了!

自身是80后,出生及长大都以多少地方。从小到深,父母还教育我们若好好读书,这是解放唯一的不二法门。那时起全校的事态来拘禁,也着实是穷人家的子女比较富二替代认真仔细,平均成绩呢要好同一不胜段。

就段更之影响是如此的大,以至于很丰富时里我都觉着,普通家庭会比富裕家庭重新厚教育。直到这几年,才发现事态来了根本性的逆转。

立即几乎年,农村人家之幼儿都几乎不念书了。我曾跟严父慈母们沟通过,了解及因主要出如下几只:

1、就算你念了本科,毕业后或者如摸索工作,工资呢就算几千片钱。可今天初中高中毕业后虽到城里打工,一个月为是以几千片,没多老分别,那还要看干啥啊?

2、农村现在新风不好,耕田种地养猪的食指越来越少了,大部分丁犹不事生产(被机械化生产冲击后未合算了),赌博的风而盛,那钱由哪来?只能望小孩寄。

您晤面意识,穷家所谓的“重视教育”其实是蛮便宜的,就是致富养家。过去华夏工作岗位少要求大,想赚只有看华山平长达总长,是相同种消极的“重视教育”。

现今中华经济便捷提高,人口老龄化又严重,就招“没有学历、能力一般”的小伙吧得透过出售劳动力得到正确的低收入。有矣扭亏的门道,读书成长就是成为备选方案了。

会不争辩经济收入,本质上真正尊重教育之,说到底还是经济比较有钱的门。

因而在社会阶层的接力赛中:穷人们同样开始就丢掉赛不走了,富人们始终在齐飞奔。这种情景下,何谈阶层突破,何谈屌丝逆袭?

04

前景见面咋样?

自家刚上大学那会儿就来段时日老自卑:作为一如既往名为地方的优等生,突然发现同学还可以用计算机制作大精彩之网页了,自己连鼠标都还不极端会就此。这单是一方面,就算在自以为傲的英语、数学、物理等几所有方,我发现自己和班里的森同学相比都起高大的异样。

即时还可知晓也地面经济蓬勃程度不同造成的差别,而且大部分别都反映于得后天习得的力方面,是得赶上的。事实上经过四年努力,在多力量方面自身倒过了绝大多数同室,也验证了就一点。

然有众多能力实际与人数所受的早期教育息息相关,是长大后哪怕还开足马力还不便追赶的。比如音乐、大部分之走能力、逻辑思维能力等等,这即代表你的降生或控制了若的能力。

富商比穷人更发出资源、更重视教育、更有行动力,这种现象还持续十年,我们的前途会面怎么样?

此前俺们认识一个有钱二代表或会见“肃然起敬”,觉得他好吗一定非常有钱,可为仅此而已。可当明天,我们敬佩的或者是,这个富有二代表有好大概率在各级面力量达合超过自己。

甭以为这是无稽之谈。如果你瞧过去十年用到高考状元头衔学生的门背景情况,会意识“富裕人家有牛人”是单明确的大势。

今日,即便出身又普通,我们还可以说经过友好之全力、拼搏去获取未来。可即使连这样的机以未来且见面转换得越来越少,因为用劲谁还足以做到,财富与能力的歧异却用几辈子之着力才能够赶上。

创业你并线不过人家,就连好的工作岗位你还拼不过人家。未来或过多合作社招人不仅看能力,还要扣你的家庭出身。

事实上这种状况在少数少数行业、企业曾经冒出了,比如很多尽人皆知的国际投行、咨询公司,能跻身的初员工要是高等别的官二代表,要么是富豪家庭。你老爸是只厅级干部还没用,因为级别太没有了,人家的秘诀是低副部级以上。

可以说,作为老百姓又套在中原,从兹开头至未来之二十年应算是我们的“黄金时期”。

盖社会以在快速前进,每天还产生雅量的机会爆发式涌现;因为富人与老百姓认知能力的异样还不不可超越,教育资源的稳尚可打破。这两头对普通人来说,便是尚表示未来发要。

万一要是说逆袭的语,现在虽是逆袭的顶时期。即便非成事,也堪吧下一代开创逆袭的根底。

关于几十年之后?你看现在之欧洲、日本等发达国家年轻人的形容就算亮了,那要命可能就是是咱的前途。

-作者-

阿何,清华大学理工男,职场充电宝&唯库创始人,感性理性兼备的写作者,国内知名个人成长研究者,个人公众号:阿何有话说(ID:aheshiwo)

8月27日晚上咱们发出一样堂在线公开课,赵正宝先生解读“以房产也投资为主的时代正式结束了”,那未来的投资核心凡是啊呢?迎接大家8月27日到自己之在线公开课。

提请须知:

1、有班主任的学习者,请直接跟自己之班主任报名。

2、没有班主任的,在左下方“阅读原文”中登记申请。

   3. 预备标注数据( MUCT 数据集)

  为了让本章的标号工作易得轻松局部,可采用公开之
MUCT 数据集。这个数目集由
3755 张人脸图像构成,每张人脸有76只点作为标志。数据集的图像是以不同光照条件与首姿势下拍摄之食指,他们来自不同年龄以及种。

  该数据集只包含了标注点,需要打定义连通性和对称性。标注连通性和针对性称性之后效果如下图左,标注数据储存在 annotations.yaml 中,如下图右:

   
 图片 5   
  图片 6

  

  visualize_annotations.cpp 实现对数据集可视化操作,关键代码如下:

图片 7图片 8

 1 cout << "n images: " << data.imnames.size() << endl
 2     << "n points: " << data.symmetry.size() << endl
 3     << "n connections: " << data.connections.size() << endl;
 4 // 可视化标注数据
 5 namedWindow("Annotations");
 6 int index = 0; bool flipped = false;
 7 while(1){
 8 Mat image;
 9 if(flipped)image = data.get_image(index,3);
10 else image = data.get_image(index,2);            // 背景图片
11 data.draw_connect(image,index,flipped);            // 连通
12 data.draw_sym(image,index,flipped);                // 对称
13 imshow("Annotations",image);
14 int c = waitKey(0);            // q 退出,p 下一张,o 上一张,f 翻转
15 if(c == 'q')break;
16 else if(c == 'p')index++;
17 else if(c == 'o')index--;
18 else if(c == 'f')flipped = !flipped;
19 if(index < 0)index = 0;
20 else if(index >= int(data.imnames.size()))index = data.imnames.size()-1;
21 }

可视化数据

  运行效果如下:

     
 图片 9 
  图片 10   
 图片 11

 

 

 

  2. 标明工具

   为了要生成的号能吃本章中之代码应用,可在 annotate.cpp 文件被找到一个中坚的标号工具。该工具将一个视屏流作为输入,这个视频流可以自文件要相机、使用该工具的过程有如下五只步骤:

  • 破获图像:第一步是将图像流显示在屏幕上,用户仍下
    S 键就只是挑选图像进行标注。

    • 重点代码如下:

       1 //选择图像进行标注
       2 annotation.set_capture_instructions();        // 显示帮助信息
       3 while (cam.get(CV_CAP_PROP_POS_AVI_RATIO) < 0.999999){    // 循环遍历每一帧
       4     Mat im, img; cam >> im; 
       5     annotation.image = im.clone();
       6     annotation.draw_instructions();
       7     imshow(annotation.wname, annotation.image);        // 显示当前帧
       8     int c = waitKey(0);        // 等待按键,q 退出,s 选择图像进行标注,其它任意键 下一帧
       9     if (c == 'q')break;
      10     else if (c == 's'){
      11         int idx = annotation.data.imnames.size(); char str[1024];
      12         if (idx < 10)sprintf(str, "00%d.png", idx);
      13         else if (idx < 100)sprintf(str, "0%d.png",idx);
      14         else               sprintf(str, "%d.png", idx);        // 文件名格式 三位整数.png
      15         imwrite(str, im);        // 保存该帧图像
      16         annotation.data.imnames.push_back(str);
      17         cam >> im;                // 显示下一帧
      18         imshow(annotation.wname, im);
      19     }
      20 }
      21 if (annotation.data.imnames.size() == 0)return 0;
      22 annotation.data.points.resize(annotation.data.imnames.size());
      

       

    • 运行效果: 
                                                                     
                                                                   
         图片 12 
           图片 13

  • 标明第一幅图:第二步首先用达平等步着第一幅图展现为用户,然后用户会在就幅图备受摘得跟的颜面特征位置。

    • 关键代码如下:

       1 // 标注第一幅图像
       2 setMouseCallback(annotation.wname, pp_MouseCallback, 0);
       3 annotation.set_pick_points_instructions();    // 显示帮助信息
       4 annotation.set_current_image(0);        // 选择第一幅图像
       5 annotation.draw_instructions();
       6 annotation.idx = 0;
       7 while (1){            // 在键入 q 之前,鼠标单击标注特征点
       8     annotation.draw_points();
       9     imshow(annotation.wname, annotation.image); 
      10     if (waitKey(0) == 'q')break;
      11 }
      12 if (annotation.data.points[0].size() == 0)return 0;
      13 annotation.replicate_annotations(0);    // 保存特征点位置信息
      
    • 运作效果(为检查代码,只选取三只特征点):

      
        图片 14

 

  • 标连通性:在就同步着,用户需要选择用两组点连接起来,以成立人脸模型的连通性结构

    • 首要代码如下:

       1 //标注连通性
       2 setMouseCallback(annotation.wname, pc_MouseCallback, 0);
       3 annotation.set_connectivity_instructions();    // 帮助信息
       4 annotation.set_current_image(0);
       5 annotation.draw_instructions();
       6 annotation.idx = 0;
       7 while (1){            // 在键入 q 之前,鼠标单击一组点建立连接
       8     annotation.draw_connections();
       9     imshow(annotation.wname, annotation.image); if (waitKey(0) == 'q')break;
      10 }
      11 save_ft(fname.c_str(), annotation.data);
      

       

    • 运转效果如下:

      
     图片 15

  •  标注对称性:这无异于步还是使上平等步的图像,用户用选出左右针对性如的点。

    • 要代码如下:

       1 //标注对称性
       2 setMouseCallback(annotation.wname, ps_MouseCallback, 0);
       3 annotation.initialise_symmetry(0);
       4 annotation.set_symmetry_instructions();
       5 annotation.set_current_image(0);
       6 annotation.draw_instructions();
       7 annotation.idx = 0; annotation.pidx = -1;
       8 while (1){            // 在键入 q 之前,鼠标单击特征点标注对称性
       9     annotation.draw_symmetry();
      10     imshow(annotation.wname, annotation.image); if (waitKey(0) == 'q')break;
      11 }
      12 save_ft(fname.c_str(), annotation.data);
      

       

    •  运行效果如下:

      
      图片 16

  •  标剩下的图像:重复第
    2 步到第 4 步,移动特征点使特征点对诺特征位置

    • 最主要代码如下:

       1 //标注剩下的图像
       2 if (type != 2){
       3     setMouseCallback(annotation.wname, mv_MouseCallback, 0);
       4     annotation.set_move_points_instructions();        // 帮助信息
       5     annotation.idx = 1; annotation.pidx = -1;
       6     while (1){
       7         annotation.set_current_image(annotation.idx);
       8         annotation.draw_instructions();
       9         annotation.set_clean_image();        // 背景图
      10         annotation.draw_connections();        // 连线
      11         imshow(annotation.wname, annotation.image);
      12         int c = waitKey(0);        // q 退出,p 下一幅图像,o 上一幅图像
      13         if (c == 'q')break;
      14         else if (c == 'p'){ annotation.idx++; annotation.pidx = -1; }
      15         else if (c == 'o'){ annotation.idx--; annotation.pidx = -1; }
      16         if (annotation.idx < 0)annotation.idx = 0;
      17         if (annotation.idx >= int(annotation.data.imnames.size()))
      18             annotation.idx = annotation.data.imnames.size() - 1;
      19     }
      20 }
      21 save_ft(fname.c_str(), annotation.data);
      

       

    • 运作效果如下:

      
      图片 17

 

  该工具将标数据存储到
ann.yaml 中,如下:

     
                  图片 18

数量收集:图像和视频标注

  现代人脸跟踪技术几乎全是数量令,即用来检测图像中面部特征位置的算法依靠面部特征的外观模型与几哪里依赖性,该依赖性来自样本集中人脸间的对立位置。样本集越怪,算法就再次具鲁棒性,因为人口脸所展现来之成形范围就再也清楚。因此,构建人脸跟踪算法的首先步是开创用于进行图像/视频的号工具,用户可用之工具来指定在每个样本图中怀念要之脸部特征位置。

  1. ### 训练数据类型

  训练人脸跟踪算法的数量貌似由以下四有些组成:

    • 图像:这部分凡是富含全体人脸图像(图像或看频帧)的聚合
    • 标明:这有行使手工方法标明每幅图像中受钉的脸部特征的相对位置
    • 本着如性索引:这有的对定义了双方对如特征的面特征点都封存了一个号码,以便用来镜像训练图像,可使得地给训集大小增加一倍
    • 连通性索引:这有的凡是同等组标注的目录对,它们定义了面孔特征的语义解释。连通性对可视化跟踪结果非常有因此

  这四独零件的可视化情形显示在产图中,从左到右依次是固有图像、脸部特征标注、颜色编码的双边对称点、镜像图像和相应标注、面部特征的连通性。

   
  图片 19

 

  为了方便管理这种数量,需兑现所有读写功能的类。本章将应用于
ft_data.hpp 头文件中定义的
ft_data 类,它是准面部跟踪数据的特色专门计划之。所有因素都定义成类的国有成员变量,如下所示

1 class ft_data{                             //人脸跟踪数据
2 public:
3   vector<int> symmetry;                    // 人脸特征点的索引,维数与用户定义的特征点数一样
4   vector<Vec2i> connections;               // 定义一对连通的面部特征
5   vector<string> imnames;                  // 存储每个图像文件名
6   vector<vector<Point2f> > points;         // 存储特征点的位置
7   ...
8 }

 

 

  ft_data 类实现了不少拜数的有用方法。为了看数据集的图像,可用
get_image 函数加载图像。使用该函数得点名加载图像的索引 idx
,以及是否拿图像为 y 轴做镜像。该函数实现如下:

 1 Mat
 2 ft_data::
 3 get_image(const int idx,    // 图像索引
 4       const int flag)        // 0=gray,1=gray+flip,2=rgb,3=rgb+flip
 5 {
 6   if((idx < 0) || (idx >= (int)imnames.size()))return Mat();
 7   Mat img,im;
 8   if(flag < 2)img = imread(imnames[idx],0);        // gray
 9   else img = imread(imnames[idx],1);            // rgb
10   if(flag % 2 != 0)flip(img,im,1);                // 以 y 轴做镜像
11   else im = img;
12   return im;
13 }

 

 

  为了通过点名的目来取相应图像的一个点集,可下
get_points 函数由此镜像索引来得到一个根据浮点的坐标向量

 1 vector<Point2f>
 2 ft_data::
 3 get_points(const int idx,        // 相应图像的索引
 4        const bool flipped)        // 是否以 y 轴做镜像
 5 {
 6   if((idx < 0) || (idx >= (int)imnames.size()))return vector<Point2f>();
 7   vector<Point2f> p = points[idx];
 8   if(flipped){        // 以 y 轴做镜像
 9     Mat im = this->get_image(idx,0);    // im 用来获取图像的宽度
10     int n = p.size(); vector<Point2f> q(n);
11     for(int i = 0; i < n; i++){            // 沿竖直方向翻转    
12       q[i].x = im.cols-1-p[symmetry[i]].x;
13       q[i].y = p[symmetry[i]].y;
14     }return q;
15   }else return p;
16 }

 

 

  ft_data 类还落实了一个函数
rm_incomplete_samples,该函数删除集合中莫展开对应标注的样书,具体落实如下:

 1 void
 2 ft_data::
 3 rm_incomplete_samples()        // 删除集合中没有进行相应标注的样本
 4 {
 5   int n = points[0].size(),N = points.size();
 6   // 找出标注数最多的样本,作为标准样本
 7   for(int i = 1; i < N; i++)n = max(n,int(points[i].size()));    
 8   for(int i = 0; i < int(points.size()); i++){
 9     if(int(points[i].size()) != n){        // 样本标注点的数量小于标准样本标注点数,从样本中删除
10       points.erase(points.begin()+i); imnames.erase(imnames.begin()+i); i--;
11     }else{
12       int j = 0;
13       for(; j < n; j++){
14         // 若点的(x,y)存在小于0,则可认为它在相应的图像中不存在
15         if((points[i][j].x <= 0) || (points[i][j].y <= 0))break;
16       }
17       if(j < n){    // 从样本中删除
18     points.erase(points.begin()+i); imnames.erase(imnames.begin()+i); i--;
19       }
20     }
21   }
22 }

 

 

  ft_data 类还落实了函数 read 和 write 的序列化,这样虽得一本万利地蕴藏和加载该类。

图片 20图片 21

 1 void 
 2 ft_data::
 3 write(FileStorage &fs) const
 4 {
 5   assert(fs.isOpened()); 
 6   fs << "{";
 7   fs << "n_connections" << (int)connections.size();        // 面部特征的语义解释
 8   for(int i = 0; i < int(connections.size()); i++){
 9     char str[256]; const char* ss;
10     sprintf(str,"connections %d 0",i); ss = str; fs << ss << connections[i][0];
11     sprintf(str,"connections %d 1",i); ss = str; fs << ss << connections[i][1];
12   }
13   fs << "n_symmetry" << (int)symmetry.size();            // 特征点的索引
14   for(int i = 0; i < int(symmetry.size()); i++){
15     char str[256]; const char* ss;
16     sprintf(str,"symmetry %d",i); ss = str; fs << ss << symmetry[i];
17   }
18   fs << "n_images" << (int)imnames.size();                // 图像绝对路径
19   for(int i = 0; i < int(imnames.size()); i++){
20     char str[256]; const char* ss;
21     sprintf(str,"image %d",i); ss = str; fs << ss << imnames[i];
22   }
23   int n = points[0].size(),N = points.size();            // 描述人脸特征点的结构
24   Mat X(2*n,N,CV_32F); X = -1;
25   for(int i = 0; i < N; i++){
26     if(int(points[i].size()) == n){
27       for(int j = 0; j < n; j++){
28     X.at<float>(2*j  ,i) = points[i][j].x;
29     X.at<float>(2*j+1,i) = points[i][j].y;
30       }
31     }
32   }
33   fs << "shapes" << X << "}";
34 }
35 //==============================================================================
36 void
37 ft_data::
38 read(const FileNode& node)
39 {
40   assert(node.type() == FileNode::MAP);
41   int n; node["n_connections"] >> n; connections.resize(n);
42   for(int i = 0; i < n; i++){
43     char str[256]; const char* ss;
44     sprintf(str,"connections %d 0",i); ss = str; node[ss] >> connections[i][0];
45     sprintf(str,"connections %d 1",i); ss = str; node[ss] >> connections[i][1];
46   }
47   node["n_symmetry"] >> n; symmetry.resize(n);
48   for(int i = 0; i < n; i++){
49     char str[256]; const char* ss;
50     sprintf(str,"symmetry %d",i); ss = str; node[ss] >> symmetry[i];
51   }
52   node["n_images"] >> n; imnames.resize(n);
53   for(int i = 0; i < n; i++){
54     char str[256]; const char* ss;
55     sprintf(str,"image %d",i); ss = str; node[ss] >> imnames[i];
56   }
57   Mat X; node["shapes"] >> X; int N = X.cols; n = X.rows/2; 
58   points.resize(N);
59   for(int i = 0; i < N; i++){
60     points[i].clear();
61     for(int j = 0; j < n; j++){
62       Point2f p(X.at<float>(2*j,i),X.at<float>(2*j+1,i));
63       if((p.x >= 0) && (p.y >= 0))points[i].push_back(p);
64     }
65   }
66 }

read write

 

 

 

  为对数据集进行可视化操作,
ft_data 实现了好多用于绘图的函数。

图片 22图片 23

  1 void
  2 ft_data::
  3 draw_points(Mat &im,
  4         const int idx,
  5         const bool flipped,
  6         const Scalar color,
  7         const vector<int> &pts)
  8 {
  9   if((idx < 0) || (idx >= (int)imnames.size()))return;
 10   int n = points[idx].size();
 11   if(pts.size() == 0){
 12     for(int i = 0; i < n; i++){
 13       if(!flipped)circle(im,points[idx][i],1,color,2,CV_AA);
 14       else{
 15     Point2f p(im.cols - 1 - points[idx][symmetry[i]].x,
 16           points[idx][symmetry[i]].y);
 17     circle(im,p,1,color,2,CV_AA);
 18       }
 19     }
 20   }else{
 21     int m = pts.size();
 22     for(int j = 0; j < m; j++){
 23       int i = pts[j]; if((i < 0) || (i >= n))continue;
 24       if(!flipped)circle(im,points[idx][i],1,color,2,CV_AA);
 25       else{
 26     Point2f p(im.cols - 1 - points[idx][symmetry[i]].x,
 27           points[idx][symmetry[i]].y);
 28     circle(im,p,1,color,2,CV_AA);
 29       }
 30     }
 31   }
 32 }
 33 //==============================================================================
 34 void
 35 ft_data::
 36 draw_sym(Mat &im,
 37      const int idx,
 38      const bool flipped,
 39      const vector<int> &pts)
 40 {
 41   if((idx < 0) || (idx >= (int)imnames.size()))return;
 42   int n = points[idx].size();
 43   RNG rn; vector<Scalar> colors(n); 
 44   for(int i = 0; i < n; i++)colors[i] = Scalar::all(0.0);
 45   for(int i = 0; i < n; i++){
 46     if(colors[i] == Scalar::all(0.0)){
 47       colors[i] = Scalar(rn.uniform(0,255),rn.uniform(0,255),rn.uniform(0,255));
 48       colors[symmetry[i]] = colors[i];
 49     }
 50   }
 51   vector<Point2f> p = this->get_points(idx,flipped); 
 52   if(pts.size() == 0){
 53     for(int i = 0; i < n; i++){circle(im,p[i],1,colors[i],2,CV_AA);}
 54   }else{
 55     int m = pts.size();
 56     for(int j = 0; j < m; j++){
 57       int i = pts[j]; if((i < 0) || (i >= n))continue;
 58       circle(im,p[i],1,colors[i],2,CV_AA);
 59     }
 60   }
 61 }
 62 //==============================================================================
 63 void
 64 ft_data::
 65 draw_connect(Mat &im,
 66          const int idx,
 67          const bool flipped,
 68          const Scalar color,
 69          const vector<int> &con)
 70 {
 71   if((idx < 0) || (idx >= (int)imnames.size()))return;
 72   int n = connections.size();
 73   if(con.size() == 0){    
 74     for(int i = 0; i < n; i++){
 75       int j = connections[i][0],k = connections[i][1];
 76       if(!flipped)line(im,points[idx][j],points[idx][k],color,1);
 77       else{
 78     Point2f p(im.cols - 1 - points[idx][symmetry[j]].x,
 79           points[idx][symmetry[j]].y);
 80     Point2f q(im.cols - 1 - points[idx][symmetry[k]].x,
 81           points[idx][symmetry[k]].y);
 82     line(im,p,q,color,1);
 83       }
 84     }
 85   }else{
 86     int m = con.size();
 87     for(int j = 0; j < m; j++){
 88       int i = con[j]; if((i < 0) || (i >= n))continue;
 89       int k = connections[i][0],l = connections[i][1];
 90       if(!flipped)line(im,points[idx][k],points[idx][l],color,1);
 91       else{
 92     Point2f p(im.cols - 1 - points[idx][symmetry[k]].x,
 93           points[idx][symmetry[k]].y);
 94     Point2f q(im.cols - 1 - points[idx][symmetry[l]].x,
 95           points[idx][symmetry[l]].y);
 96     line(im,p,q,color,1);
 97       }
 98     }
 99   }
100 }

打图函数