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的名字:

01 enum 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  
016 static 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 //在控制台打印出标记点
028 static 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 //标定出这些点
036 static 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 //画出标记点,连线形式
044 static 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  
060 int 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

猜你喜欢

转载自blog.csdn.net/qq_26570133/article/details/79079384