韦德国际官网1946 6

人脸特征点练习,iOS单元测量检验

韦德国际官网1946 1

原文地址:iOS单元测试:Specta + Expecta + OCMock + OHHTTPStubs +
KIF

  • SDM方法是13年在文章《Supervised Descent Method and its Applications
    to Face Alignment》提出的,github上一个大牛根据文章《Random
    Cascaded-Regression Copse for Robust Facial Landmark
    Detection》的方法利用SDM的思想实现了人脸特征点检测的任务。

  • SDM方法有点老,但是今天啥也不想干了,又有点无聊,就写下这篇博客,上面的文章google能下到,SDM代码也可以在github上下到,本博客主要是讲怎么用该训练出脸的特征点检测模型。作者的代码把用到的第三方库基本都包含在工程中了,但是要另外安装Eigen和OpenCV。

  • 博主是在ubuntu14.04下训练的,步骤如下:

    1. 安装Eigen库,详细方法见博主另一篇博客,ubuntu14.04+eigen3安装,博主安装的是3.3.0版本

    2. 安装OpenCV库,从OpenCV官网下载OpenCV,该页面有各种版本,而且支持各种系统,博主用的是OpenCV3.0版本中的OpenCV
      for Linux/Mac,安装方法在下载页面中有个THE INSTALLATION
      DOCUMENTATION(如下图所示),根据该文档安装即可,安装完后在终端输入pkg-config --modversion opencv
      查看安装版本,而且能查到说明OpenCV安装成功。

      韦德国际官网1946 2

      图一.jpg

    3. 下载github上的代码,在终端运行下列语句(博主直接下载到主目录下即~目录下)

      git clone https://github.com/patrikhuber/superviseddescent.git
      
    4. 下载训练用到的标定好的人脸数据库,博主用的是lpfw库,下载地址

      韦德国际官网1946 3

      图二.jpg

      博主在第三步下载下的superviseddescent的目录中创建train_data文件夹中(即~/superviseddesecent/train_data),并解压。

    5. 将一些训练的配置文件考入train_data文件夹中,运行下列代码

      cd ~/superviseddescent/train_data
      cp  ../apps/rcr/data/mean_ibug_lpfw_68.txt  ../apps/rcr/data/rcr_eval.cfg  ../apps/rcr/data/rcr_training_22.cfg  .
      

      上面三个文件,mean_ibug_lpfw_68.txt中保存的是lfpw数据库中所有图像68个标定点的归一化后的坐标值,rcr_eval.cfg保存的是用来检测训练误差选择的检测点,rcr_training_22.cfg中保存的是根据下图人脸标定规则顺序选择出来的需要训练的点。

在github上下载了一个c++的代码,用到了eigen库,代码的坐着说是运行正确了,我看着程序想要实现的功能是没有问题的,但是vs报错了。。。。。。球球大佬帮忙看一下,实在是找不出错哪了SeverityCodeDescriptionProjectFileLineSuppressionStateErrorC2338THE_EVAL_EVALTO_FUNCTION_SHOULD_NEVER_BE_CALLED_FOR_DENSE_OBJECTSProject1d:eigeneigensrccore韦德国际官网1946,densebase.h586

框架选择

    ![](https://upload-images.jianshu.io/upload_images/6983308-4eb252d71f0b4de6.jpg)

    图三.jpg

    具体训练多少个点,可以根据自己的需要设置,博主修改了rcr_training_22.cfg文件中的参数,训练了68个特征点,修改配置文件运行下列代码

        cd  ~/superviseddescent/train_data
        mv  rcr_training_22.cfg  rcr_training_68.cfg
        vim rcr_training_68.cfg

    用vim打开复制的文件rcr_training_68.cfg后修改里面的参数,博主修改的参数如下:

        modelLandmarks ; Parameters specifying the model that is to be trained
        {
        landmarks ; What landmarks to use and how we name them. Also, the model will have the landmarks in this order. 
           { ; we use the numbering from ibug-lfpw
             1 ; 1 to 8 are the right contour landmarks
             2 ;
             3 ;
             4 ;
             5 ;
             6 ;
             7 ;
             8 ;
             9 ; chin bottom
            10 ; 10 to 17 are the left contour landmarks
            11 ;
            12 ;
            13 ;
            14 ;
            15 ;
            16 ;
            17 ;
            18 ; right eyebrow outer-corner (18)
            19 ; right eyebrow between middle and outer corner
            20 ; right eyebrow middle, vertical middle (20)
            21 ; right eyebrow between middle and inner corner
            22 ; right eyebrow inner-corner (19)
            23 ; left eyebrow inner-corner (23)
            24 ; left eyebrow between inner corner and middle
            25 ; left eyebrow middle (24)
            26 ; left eyebrow between middle and outer corner
            27 ; left eyebrow outer-corner (22)
            28 ; bridge of the nose (parallel to upper eye lids)
            29 ; middle of the nose, a bit below the lower eye lids
            30 ; above nose-tip (1cm or so)
            31 ; nose-tip (3)
            32 ; right nostril, below nose, nose-lip junction
            33 ; nose-lip junction
            34 ; nose-lip junction (28)
            35 ; nose-lip junction
            36 ; left nostril, below nose, nose-lip junction
            37 ; right eye outer-corner (1)
            38 ; right eye pupil top right (from subject's perspective)
            39 ; right eye pupil top left
            40 ; right eye inner-corner (5)
            41 ; right eye pupil bottom left
            42 ; right eye pupil bottom right
            43 ; left eye inner-corner (8)
            44 ; left eye pupil top right
            45 ; left eye pupil top left
            46 ; left eye outer-corner (2)
            47 ; left eye pupil bottom left
            48 ; left eye pupil bottom right
            49 ; right mouth corner (12)
            50 ; upper lip right top outer
            51 ; upper lip middle top right
            52 ; upper lip middle top (14)
            53 ; upper lip middle top left
            54 ; upper lip left top outer
            55 ; left mouth corner (13)
            56 ; lower lip left bottom outer
            57 ; lower lip middle bottom left
            58 ; lower lip middle bottom (17)
            59 ; lower lip middle bottom right
            60 ; lower lip right bottom outer
            61 ; right inner corner of the mouth
            62 ; upper lip right bottom outer
            63 ; upper lip middle bottom
            64 ; upper lip left bottom outer
            65 ; left inner corner of the mouth
            66 ; lower lip left top outer
            67 ; lower lip middle top
            68 ; lower lip right top outer
          }
        }

6.  由于该方法运用的是OpenCV的人脸检测器进行检测人脸,所以训练的时候要使用到OpenCV的人脸检测模型。用locate语句查找haarcascade_frontalface_alt2.xml人脸检测模型,找到后拷到~/superviseddescent/train_data路径下。下语句中的PATH根据你找到的haarcascade_frontalface_alt2.xml路径书写。

        cd  ~/superviseddescent/train_data
        locate haarcascade_frontalface_alt2.xml
        cp  /PATH/haarcascade_frontalface_alt2.xml  .

7.  数据准备完毕,可以开始编译代码了

        cd  ~/superviseddescent
        mkdir  build
        cd  build
        cmake  ..
        make  -j6

    编译完成后在可以得到可执行程序进行训练,运行代码

        cd  ~/superviseddescent/build/apps/rcr
        ./rcr-train  --data  ../../../train_data/trainset/  
        -m  ../../../train_data/mean_ibug_lfpw_68.txt  
        -f  ../../../train_data/haarcascade_frontalface_alt2.xml  
        -c  ../../../train_data/rcr_training_68.cfg  
        -e  ../../../train_data/rcr_eval.cfg  
        -o  ../../../train_data/model68.bin  
        -t  ../../../train_data/testset/

操作完成后,程序开始训练,输出的特征点检测模型为model68.bin。

下面附上github代码链接:

参考这篇选型文章,http://zixun.github.io/blog/2015/04/11/iosdan-yuan-ce-shi-xi-lie-dan-yuan-ce-shi-kuang-jia-xuan-xing/,虽然结论不一定完全适用,但是关于框架对比的地方还是值得阅读的,基于这篇文章,排除Kiwi框架之后,决定参考一些项目的源代码,了解他们使用的测试方面的框架。

  • 最后给出博主的github,只是单纯的加了一个train_data文件夹,并且复制好了训练需要的文件,只要安装opencv和eigen库后,git
    clone这份代码,最后按照博客下载数据集,即可训练。

首先,参考https://github.com/artsy/eigen开源项目,其内部整体结构非常完整,开发流程也非常专业,至少比我知道的大多数国内团队都要专业:

eigen: Specta + OCMock + Expecta + OHHTTPStubs + FBSnapshotTestCase +
“Expecta+Snapshots” + “XCTest+OHHTTPStubSuiteCleanUp”。

其次,参考公司内部别的项目使用情况,发现使用以下框架来做测试方面的事情:
Specta + Expecta + OCMock + OHTTPStubs + KIF(UI Test)

so,我决定选择 Specta (BDD框架) + Expecta(断言框架) + OCMock(mock框架) +
OHHTTPStubs(http stub框架) + KIF(UI Test) 做测试框架来学习。

XCTest简介

由于我决定不直接使用XCTest作为测试框架,但是又由于Specta是基于XCTest进行封装的,所以对XCTest做一个基础的了解还是有必要的。

参考:

1.https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/testing_with_xcode

2.http://www.objc.io/issues/15-testing/xctest/,翻译:http://objccn.io/issue-15-2/

3.http://zixun.github.io/blog/2015/04/16/iosdan-yuan-ce-shi-xi-lie-dan-yuan-ce-shi-bian-ma-gui-fan/

BDD框架 — Specta

1. 简介

目前主流的BDD框架,这些BDD框架在语法层面几乎是相同的,主要的区别在于他们的可配置能力和绑定的组件。下面三个OC
BDD框架相对于官方框架XCTest都具有更好的可读性,另外现在已经有了比较流行的swift
BDD框架: https://github.com/railsware/Sleipnir和https://github.com/Quick/Quick。

https://github.com/specta/specta

https://github.com/kiwi-bdd/Kiwi

https://github.com/pivotal/cedar

关于specta与kiwi框架的对比,参考:http://appleprogramming.com/blog/2014/01/18/tdd-with-specta-and-expecta/,这篇文章的结论是specta相对于kiwi有更加优雅的语法,对于我这种刚开始使用的新手,果断采用specta这种各种完胜的框架。Specta框架具有一下特点:

An OC RSpec-like BDD DSL

Quick and easy set up

Build on top of XCTest

Excellent Xcode integration

2. Specta BDD DSL语法简介

可以参考https://github.com/specta/specta官网和https://github.com/artsy/eigen项目中的test
case代码来学习语法

1) SpecBegin 声明了一个测试类,SpecEnd 结束了类声明

2) describe (context) 块声明了一组实例

3) it (example/specify) 是一个单一的例子

4) beforeAll 是一个运行于所有同级块之前的块,只运行一次。afterAll
与beforeAll相反,是在所有同级块之后运行的块,只运行一次。

5)
beforeEach/afterEach,在每个同级块运行的时候,都会运行一次,而beforeAll/afterAll只会运行一次

6)
it/waitUntil/done(),异步调用,注意完成异步操作之后,必须调用done()函数,如下:

it(@”should do some stuff asynchronously”, ^{

waitUntil(^(DoneCallback done) {

// Async example blocks need to invoke done() callback.

done();

});

});

7) sharedExamplesFor
和 itShouldBehaveLike结合在一起,可以实现在不同的spec之间共享同一套test
case,参考:http://artandlogic.com/2014/02/specta-shared-behavior/;sharedExamplesFor
设置多个spec之间共享的test
case,第一个参数作为标识符。通过itShouldBehaveLike来运行spec中test
case,第一个参数传入sharedExamplesFor设置时使用的标识符。注意,在describe局部使用sharedExamplesFor定义shared
examples,可以在它作用域内覆盖全局的shared examples。

8)
pending,只打印一条log信息,不做测试。这个语句会给出一条警告,可以作为一开始集中书写行为描述时还未实现的测试的提示。

断言框架 — Expecta

用法可以参考开源项目:https://github.com/artsy/eigen,从中找到对应的代码学习是最好的方式。如果需要找到更多更全面的用法,可以去项目官方网站:https://github.com/specta/expecta。截取一段eigen上面代码,基本上就可以了解Expecta框架的基本用法了,如下图中
expect(mapView.nextZoomScale).to.equal(mapView.annotationZoomScaleThreshold)

韦德国际官网1946 4

mock框架 —OCMock

了解OCMock
2.x中的详细features,可以参考:http://ocmock.org/features/;了解OCMock
3.x的详细API,可以参考:http://ocmock.org/reference/;

什么是mock,refer: http://hackazach.net/code/2014/03/03/effective-testing-with-ocmock/:

In a modern Object Oriented system, the component under test will likely have several object dependencies. Instead of instantiating dependencies as concrete classes, we use mocks. Mocks are ‘fake’ objects with pre-defined behavior to stand-in for concrete objects during testing. The component under test does not know the difference! With mocks, a component can be tested with confidence that it behaves as designed within a larger system. 

OCMock框架的用法也比较简单,由于我个人时间比较紧张,只能抽出一两天的时间学习测试部分的知识,就不多说了,下面几篇文章都说的比较清楚,可以参考:http://zixun.github.io/blog/2015/04/16/iosdan-yuan-ce-shi-xi-lie-yi-ocmockchang-jian-shi-yong-fang-shi/,学习2.x和3.x的API的基本使用。另外可以参考开源项目https://github.com/artsy/eigen,学习其中的OCMock
API的使用,框架使用比较简单,看看就懂了,不需要多说。

eigen的一个test

case,注意在执行完毕的时候,需要调用stopMocking。OCMockObject是基于runtime方法转发实现的,mock一个对象,就是对这个对象的方法进行转发的过程,执行完毕需要调用stopMocking,否则会影响其他test

case的执行。下面可以看出一个OCMock基本过程:获得OCMockObject ->
stub方法 -> 设置expect

->

verify校验执行结果 -> 调用stopMocking

韦德国际官网1946 5

下面有一个mock一个alert view show的过程

韦德国际官网1946 6

参考:

http://ocmock.org/reference/

http://ocmock.org/features/

http://ocmock.org/introduction/

http://www.archive.alexvollmer.com/posts/2010/06/28/making-fun-of-things-with-ocmock/

http://hackazach.net/code/2014/03/03/effective-testing-with-ocmock/,翻译:http://zixun.github.io/blog/2015/04/16/iosdan-yuan-ce-shi-xi-lie-yi-ocmockchang-jian-shi-yong-fang-shi/

Improving iOS Unit Tests with OCMock

OHHTTPStubs

官方:https://github.com/AliSoftware/OHHTTPStubs。这个框架是基于NSURLProtocol实现的,之前正好看过这部分的只是,整理来说,这个框架的源代码并不复杂,但实现还是比较巧妙的。详细的介绍和使用,在github上面介绍的非常清楚,框架本身使用也比较简单:

[OHHTTPStubsstubRequestsPassingTest:^BOOL(NSURLRequest*request) {

return[request.URL.hostisEqualToString:@”mywebservice.com”];

}withStubResponse:^OHHTTPStubsResponse*(NSURLRequest*request) {

// Stub it with our “wsresponse.json” stub file (which is in same bundle as self)

NSString* fixture =OHPathForFile(@”wsresponse.json”, self.class);

return[OHHTTPStubsResponseresponseWithFileAtPath:fixture

statusCode:200headers:@{@”Content-Type”:@”application/json”}];

}];

这个框架的主要使用方法就是上面这个示例,用法很明显易用。结合unit

test使用的时候,需要使用网络请求的时候,可以在it或者beforeAll或者beforeEach的时候进行stub

request,即上面这段代码的行为。但是不要忘记的是,需要在tear
down的时候,即specta的afterAll的时候,记得调用

[OHHTTPStubs removeAllStubs] 。

注意,这里只是使用NSURLProtocol来stub

request,不会影响被测试的请求接口的测试,请求是异步的话,可以使用Specta的it/waitUntil/done()流程对请求进行测试,如果使用XCTest的话,OHTTPStubs给出了一个wiki解决,使用XCTestExpectation来搞定,我觉得挺有意思:

– (void)testFoo

{

NSURLRequest* request = …

XCTestExpectation* responseArrived = [selfexpectationWithDescription:@”response of async request has arrived”];

__blockNSData* receivedData =nil;

[NSURLConnectionsendAsynchronousRequest:request

queue:[NSOperationQueuemainQueue]

completionHandler:^(NSURLResponse* response,NSData* data,NSError* error)

{

receivedData = data;

[responseArrivedfulfill];

}

];

[selfwaitForExpectationsWithTimeout:timeouthandler:^{

// By the time we reach this code, the while loop has exited

// so the response has arrived or the test has timed out

XCTAssertNotNil(receivedData,@”Received data should not be nil”);

}];

}

由于NSURLProtocol的局限性,OHHTTPStubs没法用来测试background
sessions和模拟数据上传。

F.I.R.S.T 原则

优秀测试实践原则,https://pragprog.com/magazines/2012-01/unit-tests-are-first:

Fast — 测试应该能够被经常执行

Isolated — 测试本身不能依赖于外部因素或其他测试的结果

Repeatable — 每次运行测试都应该产生相同的结果

Self-verifying — 测试应该依赖于断言,不需要人为干预

Timely — 测试应该和生产代码一同书写

如何将测试结果收益最大化:不要将测试和实现细节耦合在一起。

不要测试私有方法

不要Stub私有方法

不要Stub外部库

正确地Stub依赖

不要测试构造函数

参考资料

http://www.objc.io/issues/15-testing/,(翻译:http://objccn.io/issue-15/)

https://github.com/artsy/eigen,非常专业的APP的开源代码,http://objccn.io/issue-22-2/

: RAC + 单元测试

http://www.jianshu.com/p/73f9d719cee4

http://nshipster.com/unit-testing/

http://onevcat.com/2014/02/ios-test-with-kiwi/

http://onevcat.com/2014/05/kiwi-mock-stub-test/

https://github.com/dblock/fui,find
unused objective-c imports

发表评论

电子邮件地址不会被公开。 必填项已用*标注