stasm的接口和使用

(转)原文地址:http://www.aiuxian.com/article/p-656517.html

一、简介 Stasm:

1、stasm是一个c++软件包,用来定位人脸中面部的landmarks(路标,特征点)。输入带有人脸图像,返回landmarks的位置。

2、Stasm被设计工作在大约垂直(竖直)且带有中性表情的直视的人脸。对于生气或者带有表情的将得到不好的效果。

3、Stasm采用的HAT(Histogram Array Transform)描述子来做模版匹配,类似与SIFT描述子。


二、测试和运行

用vs 2010打开 stasm4.0.0\vc10目录下的minimal.sln文件,进入工程后,假如opencv的配置环境,包括(.h,lib,和dll),就可以运行看到效果。


三、Stasm的库函数

库接口定义在stasm_lib.h文件中,landmarks的名字列在stasm_landmarks.h的头文件中。


1、简单接口:

最简单的方法:使用stasm_search_single函数,该函数使用opencv的前侧人脸检测器找到图像中最大的人脸,并且返回landmarks的位置。

人脸的宽度至少是图像宽度的10%。


2、更多用途的接口:

1)一个图像中多张人脸的标定(landmarks)

2)可以找到一些连续的接口,一致的接口。

最基本的思想是:首先调用stasm_open_image函数来检测人脸,其次重复的调用stasm_search_auto函数来一个一个地landmarks(标记)人脸。具体详情参考minimal2.cpp和stasm_lib.h中的注释。


3、multiface 参数

stasm_open_image函数中的参数multiface,如果设置为1,你可以重复地调用stasm_search_auto函数,直到图像找到图像中的所有人脸(人脸检测器能够检测到的)。

如果设置为0,stasm_search_auto函数返回一个“最好”的人脸,通常是OpenCV人脸检测器检测到的最大的人脸。


4、用户部分初始化

在许多应用中,需要人为的手动矫正人脸上的一些点,使用stasm_search_pinned函数可以实现。

主要用在用户指定5个点:眼睛的外角(2个),鼻子的顶端(1个),和嘴角(2个)。


5、工具函数

1)stasm_convert_shape函数,例如:stasm_convert_shape(newlandmarks, 76);newlandmarks为float类型的数组,大小为2*stasm_NLANDMARKS,stasm_NLANDMARKS为默认值77。即可以改变寻找特征点的个数,一般为20,22,68,76和默认的77。对于其他值如40,则特征点全为0(不工作)。

2)stasm_face_points_into_image函数,是landmarks(标记, 特征点)在图像的边界内部。例如如果一个人的前额被图像的边缘剪切了,Stasm将会把landmarks(特征点)的位置定位在图像的边界外面。

3)stasm_printf 打印输出流,类似与printf函数,但也可以打印到文件stasm.log。如果stasm_init函数的trace参数设置为1,则输出stasm.log日志文件。


四、注意人脸检测实现

图片分享:

对于左侧人脸,OpenCV经常会检测不到,因为人脸太过于靠近图像的边缘。

对于左侧图像,Stasm通过人工增加边界1.2*1.2倍的大小,这样可以简单的人脸,但是也降低了检测的速度。


五、stasm_landmarks.h文件内的landmarks的名字:

01enum stasm_LANDMARKS_77 // stasm77 landmarks
02{
03    L_LTemple,          // 00
04    L_LJaw01,           // 01
05    L_LJawNoseline,     // 02 nose line on left jaw
06    L_LJawMouthline,    // 03 mouth line on left jaw
07    L_LJaw04,           // 04
08    L_LJaw05,           // 05
09    L_CTipOfChin,       // 06
10    L_RJaw07,           // 07
11    L_RJaw08,           // 08
12    L_RJawMouthline,    // 09
13    L_RJawNoseline,     // 10
14    L_RJaw11,           // 11
15    L_RTemple,          // 12
16    L_RForehead,        // 13
17    L_CForehead,        // 14
18    L_LForehead,        // 15
19    L_LEyebrowTopInner, // 16
20    L_LEyebrowTopOuter, // 17
21    L_LEyebrowOuter,    // 18
22    L_LEyebrowBotOuter, // 19
23    L_LEyebrowBotInner, // 20
24    L_LEyebrowInner,    // 21
25    L_REyebrowInner,    // 22
26    L_REyebrowTopInner, // 23
27    L_REyebrowTopOuter, // 24
28    L_REyebrowOuter,    // 25
29    L_REyebrowBotOuter, // 26
30    L_REyebrowBotInner, // 27
31    L_REyelid,          // 28
32    L_LEyelid,          // 29
33    L_LEyeInner,        // 30
34    L_LEye31,           // 31
35    L_LEyeTop,          // 32
36    L_LEye33,           // 33
37    L_LEyeOuter,        // 34
38    L_LEye35,           // 35
39    L_LEyeBot,          // 36
40    L_LEye37,           // 37
41    L_LPupil,           // 38
42    L_RPupil,           // 39
43    L_REyeInner,        // 40
44    L_REye41,           // 41
45    L_REyeTop,          // 42
46    L_REye43,           // 43
47    L_REyeOuter,        // 44
48    L_REye45,           // 45
49    L_REyeBot,          // 46
50    L_REye47,           // 47
51    L_RNoseMid,         // 48
52    L_CNoseMid,         // 49
53    L_LNoseMid,         // 50
54    L_LNostrilTop,      // 51
55    L_CNoseTip,         // 52
56    L_RNostrilTop,      // 53
57    L_RNoseSide,        // 54
58    L_RNostrilBot,      // 55
59    L_CNoseBase,        // 56
60    L_LNostrilBot,      // 57
61    L_LNoseSide,        // 58
62    L_LMouthCorner,     // 59
63    L_LMouth60,         // 60
64    L_LMouthCupid,      // 61
65    L_CTopOfTopLip,     // 62
66    L_RMouthCupid,      // 63
67    L_RMouth64,         // 64
68    L_RMouthCorner,     // 65
69    L_RMouth66,         // 66
70    L_CBotOfTopLip,     // 67
71    L_LMouth68,         // 68
72    L_LMouth69,         // 69
73    L_CTopOfBotLip,     // 70
74    L_RMouth71,         // 71
75    L_RMouth72,         // 72
76    L_RMouth73,         // 73
77    L_CBotOfBotLip,     // 74
78    L_LMouth75,         // 75
79    L_LMouth76          // 76
80};


六、测试程序

需要在调试时输入四个参数,0 25 1 i000qa-fn.jpg。

参数0表示我们只处理一张人脸

参数25表示检测到的人脸的最小宽度,这里设置为25,是为了下面的调试

参数1表示我们需要打印stasm.log日志

参数i000qa-fn.jpg表示输入的图像名字

001// test_stasm_lib.cpp: test stasm_lib.cpp
002//
003// Copyright (C) 2005-2013, Stephen Milborrow
004 
005#include <stdio.h>
006#include <stdarg.h>
007#include <stdlib.h>
008#include <time.h>
009#include "opencv/highgui.h" // needed for imread
010#include "stasm_lib.h"
011#include "stasm_lib_ext.h"  // needed for stasm_search_auto_ext
012#include "stasm_landmarks.h"
013 
014#pragma warning(disable:4996) // 'vsprintf': This function may be unsafe
015 
016static void Exit(const char* format, ...) // args like printf
017{
018    char s[1024+1];
019    va_list args;
020    va_start(args, format);
021    vsprintf(s, format, args);
022    va_end(args);
023    stasm_printf("\n%s\n", s);
024    exit(1);
025}
026 
027//在控制台打印出标记点
028static void PrintLandmarks(const float* landmarks, const char* msg)
029{
030    stasm_printf("%s:\n", msg);
031    for (int i = 0; i < stasm_NLANDMARKS; i++)
032        stasm_printf("%3d: %4.0f %4.0f\n",
033        i, landmarks[i*2], landmarks[i*2+1]);//点的位置(x,y)坐标
034}
035//标定出这些点
036static void BiaoDing(cv::Mat_<unsigned char> &img,float landmarks[],int nlandmarks=stasm_NLANDMARKS)
037{
038    for(int i=0;i<nlandmarks;i++)
039    {
040        img(cvRound(landmarks[i*2+1]),cvRound(landmarks[2*i]))=255;
041    }
042}
043//画出标记点,连线形式
044static void DrawLandmarks(
045    cv::Mat_<unsigned char>& img,
046    float                    landmarks[],
047    int                      nlandmarks = stasm_NLANDMARKS)
048{
049    for (int i = 0; i < nlandmarks-1; i++)
050    {
051        const int ix  = cvRound(landmarks[i*2]);       // this point
052        const int iy  = cvRound(landmarks[i*2+1]);
053        const int ix1 = cvRound(landmarks[(i+1)*2]);   // next point
054        const int iy1 = cvRound(landmarks[(i+1)*2+1]);
055        cv::line(img,
056            cv::Point(ix, iy), cv::Point(ix1, iy1), 255, 1);
057    }
058}
059 
060int main(int argc, const char** argv)
061{
062    if (argc != 5)
063        Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE");
064 
065    const int multi = argv[1][0] - '0';//将输入的为char*类型的参数转换为int如输入的为0则输出multi为0
066    if (multi != 0 && multi != 1)     //我们设置为0,因为我们只处理一个人脸
067        Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE, "
068        "with MULTI 0 or 1, you have MULTI %s", argv[1]);
069 
070    int minwidth = -1;
071    if (sscanf(argv[2], "%d", &minwidth) != 1 ||//设置检测的人脸的最小宽度,如果没有扫描成功,或者最小的宽度<1或者大于100则退出
072        minwidth < 1 || minwidth > 100)
073    {
074        Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE with "
075            "MINWIDTH 1 to 100,  you have MINWIDTH %s", argv[2]);
076    }
077 
078    const int trace = argv[3][0] - '0';//我们想跟踪日志,所以我们设置为1
079    if (trace < 0 || trace > 1)
080        Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE, with TRACE 0 or 1");
081 
082    if (!stasm_init("../data", trace))
083        Exit("stasm_init failed: %s", stasm_lasterr());
084 
085    const char* path = argv[4]; // image name//第五个参数(输入的四个参数),图像的名字
086    stasm_printf("Reading %s\n", path);
087    const cv::Mat_<unsigned char> img(cv::imread(path, CV_LOAD_IMAGE_GRAYSCALE));
088    if (!img.data) // could not load image?
089        Exit("Cannot load %s", path);
090 
091    cv::Mat_<unsigned char> outimg(img.clone());
092 
093    if (!stasm_open_image((const char*)img.data, img.cols, img.rows,
094        path, multi != 0, minwidth))
095        Exit("stasm_open_image failed: %s", stasm_lasterr());
096 
097    // Test stasm_search_auto.
098    // The min face size was set in the above stasm_open_image call.
099 
100    float landmarks[2 * stasm_NLANDMARKS]; // x,y coords
101    int iface = 0;
102    while (1)
103    {
104        stasm_printf("--- Auto Face %d ---\n", iface);
105        int foundface;
106        float estyaw;
107        if (!stasm_search_auto_ext(&foundface, landmarks, &estyaw))//如果没有成功运行
108            Exit("stasm_search_auto failed: %s", stasm_lasterr());
109        if (!foundface)//如果没有找到人脸,或者最后一个人脸结束
110        {
111            stasm_printf("No more faces\n");
112            break; // note break
113        }
114        char s[100]; sprintf(s, "\nFinal with auto init (estyaw %.0f)", estyaw);
115        PrintLandmarks(landmarks, s);//标记人脸
116        DrawLandmarks(outimg, landmarks);//连线人脸
117        iface++;//统计找到的人脸的个数。
118        if (trace)
119            stasm_printf("\n");
120    }
121    imwrite("test_stasm_lib_auto.bmp", outimg);
122 
123    //下面是测试用的,当设置只寻找一个人脸,且minwidh=25并且找到了人脸,则进入调试状态。
124    if (multi == 0 && minwidth == 25 && iface)
125    {
126        // Test stasm_search_pinned.  A human user is not at hand, so gyp by using
127        // points from the last face found above for our 5 start points
128 
129        stasm_printf("--- Pinned Face %d ---\n", iface);
130        float pinned[2 * stasm_NLANDMARKS]; // x,y coords
131        memset(pinned, 0, sizeof(pinned));//初始化一个pin空间和stasm_NLANDMARK一样大
132        pinned[L_LEyeOuter*2]      = landmarks[L_LEyeOuter*2] + 2;
133        pinned[L_LEyeOuter*2+1]    = landmarks[L_LEyeOuter*2+1];
134        pinned[L_REyeOuter*2]      = landmarks[L_REyeOuter*2] - 2;
135        pinned[L_REyeOuter*2+1]    = landmarks[L_REyeOuter*2+1];
136        pinned[L_CNoseTip*2]       = landmarks[L_CNoseTip*2];
137        pinned[L_CNoseTip*2+1]     = landmarks[L_CNoseTip*2+1];
138        pinned[L_LMouthCorner*2]   = landmarks[L_LMouthCorner*2];
139        pinned[L_LMouthCorner*2+1] = landmarks[L_LMouthCorner*2+1];
140        pinned[L_RMouthCorner*2]   = landmarks[L_RMouthCorner*2];
141        pinned[L_RMouthCorner*2+1] = landmarks[L_RMouthCorner*2+1];
142 
143        memset(landmarks, 0, sizeof(landmarks));//将landmarks重置为0
144        if (!stasm_search_pinned(landmarks,//利用pinned矫正landmarks
145            pinned, (const char*)img.data, img.cols, img.rows, path))
146            Exit("stasm_search_pinned failed: %s", stasm_lasterr());
147        PrintLandmarks(landmarks, "Final with pinned init");
148        outimg = img.clone();
149        DrawLandmarks(outimg, landmarks);
150        imwrite("test_stasm_lib_pinned.bmp", outimg);
151 
152        // test stasm_convert_shape,找到了位置之后的处理
153        float newlandmarks[2 * stasm_NLANDMARKS]; // x,y coords
154#if 0
155        memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float));
156        stasm_convert_shape(newlandmarks, 68);
157        PrintLandmarks(newlandmarks, "stasm77 to xm2vts");
158#endif
159#if 0
160        outimg = img.clone();
161        DrawLandmarks(outimg, newlandmarks, 68);
162        imwrite("test_stasm_lib_68.bmp", outimg);
163#endif
164#if 0
165        memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float));
166        stasm_convert_shape(newlandmarks, 76);
167        PrintLandmarks(newlandmarks, "stasm77 to stasm76");
168#endif
169#if 0
170        outimg = img.clone();
171        DrawLandmarks(outimg, newlandmarks, 76);
172        imwrite("test_stasm_lib_76.bmp", outimg);
173#endif
174 
175#if 1
176        memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float));
177        stasm_convert_shape(newlandmarks, 22);
178        PrintLandmarks(newlandmarks, "stasm77 to stasm22");
179        outimg = img.clone();
180        // DrawLandmarks(outimg, newlandmarks, 22);
181        BiaoDing(outimg,newlandmarks,22);
182        imwrite("test_stasm_lib_22.bmp_biaoding.bmp", outimg);
183 
184        memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float));
185        stasm_convert_shape(newlandmarks, 20);
186        PrintLandmarks(newlandmarks, "stasm77 to stasm20");
187        outimg = img.clone();
188        //DrawLandmarks(outimg, newlandmarks, 20);
189        BiaoDing(outimg,newlandmarks,19);
190        imwrite("test_stasm_lib_20_biaoding.bmp", outimg);
191#endif
192    }
193 
194    return 0;       // success
195}


七、运行效果

1)20个landmarks位置,分别为点图和连线图:


图片分享:
图片分享:
  


2)68个landmarks特征点

图片分享:


3)76个特征点

图片分享:


4)77个特征点(默认)

(window).load(function (){ 
                                    var imgs =
("#articleCon img"); for(var i = 0 ; i
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值