Stasm的接口和使用
2016-06-05 11:23
375 查看
(转)原文地址: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的名字:
enum stasm_LANDMARKS_77 // stasm77 landmarks
{
L_LTemple, // 00
L_LJaw01, // 01
L_LJawNoseline, // 02 nose line on left jaw
L_LJawMouthline, // 03 mouth line on left jaw
L_LJaw04, // 04
L_LJaw05, // 05
L_CTipOfChin, // 06
L_RJaw07, // 07
L_RJaw08, // 08
L_RJawMouthline, // 09
L_RJawNoseline, // 10
L_RJaw11, // 11
L_RTemple, // 12
L_RForehead, // 13
L_CForehead, // 14
L_LForehead, // 15
L_LEyebrowTopInner, // 16
L_LEyebrowTopOuter, // 17
L_LEyebrowOuter, // 18
L_LEyebrowBotOuter, // 19
L_LEyebrowBotInner, // 20
L_LEyebrowInner, // 21
L_REyebrowInner, // 22
L_REyebrowTopInner, // 23
L_REyebrowTopOuter, // 24
L_REyebrowOuter, // 25
L_REyebrowBotOuter, // 26
L_REyebrowBotInner, // 27
L_REyelid, // 28
L_LEyelid, // 29
L_LEyeInner, // 30
L_LEye31, // 31
L_LEyeTop, // 32
L_LEye33, // 33
L_LEyeOuter, // 34
L_LEye35, // 35
L_LEyeBot, // 36
L_LEye37, // 37
L_LPupil, // 38
L_RPupil, // 39
L_REyeInner, // 40
L_REye41, // 41
L_REyeTop, // 42
L_REye43, // 43
L_REyeOuter, // 44
L_REye45, // 45
L_REyeBot, // 46
L_REye47, // 47
L_RNoseMid, // 48
L_CNoseMid, // 49
L_LNoseMid, // 50
L_LNostrilTop, // 51
L_CNoseTip, // 52
L_RNostrilTop, // 53
L_RNoseSide, // 54
L_RNostrilBot, // 55
L_CNoseBase, // 56
L_LNostrilBot, // 57
L_LNoseSide, // 58
L_LMouthCorner, // 59
L_LMouth60, // 60
L_LMouthCupid, // 61
L_CTopOfTopLip, // 62
L_RMouthCupid, // 63
L_RMouth64, // 64
L_RMouthCorner, // 65
L_RMouth66, // 66
L_CBotOfTopLip, // 67
L_LMouth68, // 68
L_LMouth69, // 69
L_CTopOfBotLip, // 70
L_RMouth71, // 71
L_RMouth72, // 72
L_RMouth73, // 73
L_CBotOfBotLip, // 74
L_LMouth75, // 75
L_LMouth76 // 76
};
六、测试程序
需要在调试时输入四个参数,0 25 1 i000qa-fn.jpg。
参数0表示我们只处理一张人脸
参数25表示检测到的人脸的最小宽度,这里设置为25,是为了下面的调试
参数1表示我们需要打印stasm.log日志
参数i000qa-fn.jpg表示输入的图像名字
七、运行效果
1)20个landmarks位置,分别为点图和连线图:
2)68个landmarks特征点
3)76个特征点
4)77个特征点(默认)
学习讨论之用,联系作者删除
一、简介 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的名字:
enum stasm_LANDMARKS_77 // stasm77 landmarks
{
L_LTemple, // 00
L_LJaw01, // 01
L_LJawNoseline, // 02 nose line on left jaw
L_LJawMouthline, // 03 mouth line on left jaw
L_LJaw04, // 04
L_LJaw05, // 05
L_CTipOfChin, // 06
L_RJaw07, // 07
L_RJaw08, // 08
L_RJawMouthline, // 09
L_RJawNoseline, // 10
L_RJaw11, // 11
L_RTemple, // 12
L_RForehead, // 13
L_CForehead, // 14
L_LForehead, // 15
L_LEyebrowTopInner, // 16
L_LEyebrowTopOuter, // 17
L_LEyebrowOuter, // 18
L_LEyebrowBotOuter, // 19
L_LEyebrowBotInner, // 20
L_LEyebrowInner, // 21
L_REyebrowInner, // 22
L_REyebrowTopInner, // 23
L_REyebrowTopOuter, // 24
L_REyebrowOuter, // 25
L_REyebrowBotOuter, // 26
L_REyebrowBotInner, // 27
L_REyelid, // 28
L_LEyelid, // 29
L_LEyeInner, // 30
L_LEye31, // 31
L_LEyeTop, // 32
L_LEye33, // 33
L_LEyeOuter, // 34
L_LEye35, // 35
L_LEyeBot, // 36
L_LEye37, // 37
L_LPupil, // 38
L_RPupil, // 39
L_REyeInner, // 40
L_REye41, // 41
L_REyeTop, // 42
L_REye43, // 43
L_REyeOuter, // 44
L_REye45, // 45
L_REyeBot, // 46
L_REye47, // 47
L_RNoseMid, // 48
L_CNoseMid, // 49
L_LNoseMid, // 50
L_LNostrilTop, // 51
L_CNoseTip, // 52
L_RNostrilTop, // 53
L_RNoseSide, // 54
L_RNostrilBot, // 55
L_CNoseBase, // 56
L_LNostrilBot, // 57
L_LNoseSide, // 58
L_LMouthCorner, // 59
L_LMouth60, // 60
L_LMouthCupid, // 61
L_CTopOfTopLip, // 62
L_RMouthCupid, // 63
L_RMouth64, // 64
L_RMouthCorner, // 65
L_RMouth66, // 66
L_CBotOfTopLip, // 67
L_LMouth68, // 68
L_LMouth69, // 69
L_CTopOfBotLip, // 70
L_RMouth71, // 71
L_RMouth72, // 72
L_RMouth73, // 73
L_CBotOfBotLip, // 74
L_LMouth75, // 75
L_LMouth76 // 76
};
六、测试程序
需要在调试时输入四个参数,0 25 1 i000qa-fn.jpg。
参数0表示我们只处理一张人脸
参数25表示检测到的人脸的最小宽度,这里设置为25,是为了下面的调试
参数1表示我们需要打印stasm.log日志
参数i000qa-fn.jpg表示输入的图像名字
// test_stasm_lib.cpp: test stasm_lib.cpp // // Copyright (C) 2005-2013, Stephen Milborrow #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <time.h> #include "opencv/highgui.h" // needed for imread #include "stasm_lib.h" #include "stasm_lib_ext.h" // needed for stasm_search_auto_ext #include "stasm_landmarks.h" #pragma warning(disable:4996) // 'vsprintf': This function may be unsafe static void Exit(const char* format, ...) // args like printf { char s[1024+1]; va_list args; va_start(args, format); vsprintf(s, format, args); va_end(args); stasm_printf("\n%s\n", s); exit(1); } //在控制台打印出标记点 static void PrintLandmarks(const float* landmarks, const char* msg) { stasm_printf("%s:\n", msg); for (int i = 0; i < stasm_NLANDMARKS; i++) stasm_printf("%3d: %4.0f %4.0f\n", i, landmarks[i*2], landmarks[i*2+1]);//点的位置(x,y)坐标 } //标定出这些点 static void BiaoDing(cv::Mat_<unsigned char> &img,float landmarks[],int nlandmarks=stasm_NLANDMARKS) { for(int i=0;i<nlandmarks;i++) { img(cvRound(landmarks[i*2+1]),cvRound(landmarks[2*i]))=255; } } //画出标记点,连线形式 static void DrawLandmarks( cv::Mat_<unsigned char>& img, float landmarks[], int nlandmarks = stasm_NLANDMARKS) { for (int i = 0; i < nlandmarks-1; i++) { const int ix = cvRound(landmarks[i*2]); // this point const int iy = cvRound(landmarks[i*2+1]); const int ix1 = cvRound(landmarks[(i+1)*2]); // next point const int iy1 = cvRound(landmarks[(i+1)*2+1]); cv::line(img, cv::Point(ix, iy), cv::Point(ix1, iy1), 255, 1); } } int main(int argc, const char** argv) { if (argc != 5) Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE"); const int multi = argv[1][0] - '0';//将输入的为char*类型的参数转换为int如输入的为0则输出multi为0 if (multi != 0 && multi != 1) //我们设置为0,因为我们只处理一个人脸 Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE, " "with MULTI 0 or 1, you have MULTI %s", argv[1]); int minwidth = -1; if (sscanf(argv[2], "%d", &minwidth) != 1 ||//设置检测的人脸的最小宽度,如果没有扫描成功,或者最小的宽度<1或者大于100则退出 minwidth < 1 || minwidth > 100) { Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE with " "MINWIDTH 1 to 100, you have MINWIDTH %s", argv[2]); } const int trace = argv[3][0] - '0';//我们想跟踪日志,所以我们设置为1 if (trace < 0 || trace > 1) Exit("Usage: test_stasm_lib MULTI MINWIDTH TRACE IMAGE, with TRACE 0 or 1"); if (!stasm_init("../data", trace)) Exit("stasm_init failed: %s", stasm_lasterr()); const char* path = argv[4]; // image name//第五个参数(输入的四个参数),图像的名字 stasm_printf("Reading %s\n", path); const cv::Mat_<unsigned char> img(cv::imread(path, CV_LOAD_IMAGE_GRAYSCALE)); if (!img.data) // could not load image? Exit("Cannot load %s", path); cv::Mat_<unsigned char> outimg(img.clone()); if (!stasm_open_image((const char*)img.data, img.cols, img.rows, path, multi != 0, minwidth)) Exit("stasm_open_image failed: %s", stasm_lasterr()); // Test stasm_search_auto. // The min face size was set in the above stasm_open_image call. float landmarks[2 * stasm_NLANDMARKS]; // x,y coords int iface = 0; while (1) { stasm_printf("--- Auto Face %d ---\n", iface); int foundface; float estyaw; if (!stasm_search_auto_ext(&foundface, landmarks, &estyaw))//如果没有成功运行 Exit("stasm_search_auto failed: %s", stasm_lasterr()); if (!foundface)//如果没有找到人脸,或者最后一个人脸结束 { stasm_printf("No more faces\n"); break; // note break } char s[100]; sprintf(s, "\nFinal with auto init (estyaw %.0f)", estyaw); PrintLandmarks(landmarks, s);//标记人脸 DrawLandmarks(outimg, landmarks);//连线人脸 iface++;//统计找到的人脸的个数。 if (trace) stasm_printf("\n"); } imwrite("test_stasm_lib_auto.bmp", outimg); //下面是测试用的,当设置只寻找一个人脸,且minwidh=25并且找到了人脸,则进入调试状态。 if (multi == 0 && minwidth == 25 && iface) { // Test stasm_search_pinned. A human user is not at hand, so gyp by using // points from the last face found above for our 5 start points stasm_printf("--- Pinned Face %d ---\n", iface); float pinned[2 * stasm_NLANDMARKS]; // x,y coords memset(pinned, 0, sizeof(pinned));//初始化一个pin空间和stasm_NLANDMARK一样大 pinned[L_LEyeOuter*2] = landmarks[L_LEyeOuter*2] + 2; pinned[L_LEyeOuter*2+1] = landmarks[L_LEyeOuter*2+1]; pinned[L_REyeOuter*2] = landmarks[L_REyeOuter*2] - 2; pinned[L_REyeOuter*2+1] = landmarks[L_REyeOuter*2+1]; pinned[L_CNoseTip*2] = landmarks[L_CNoseTip*2]; pinned[L_CNoseTip*2+1] = landmarks[L_CNoseTip*2+1]; pinned[L_LMouthCorner*2] = landmarks[L_LMouthCorner*2]; pinned[L_LMouthCorner*2+1] = landmarks[L_LMouthCorner*2+1]; pinned[L_RMouthCorner*2] = landmarks[L_RMouthCorner*2]; pinned[L_RMouthCorner*2+1] = landmarks[L_RMouthCorner*2+1]; memset(landmarks, 0, sizeof(landmarks));//将landmarks重置为0 if (!stasm_search_pinned(landmarks,//利用pinned矫正landmarks pinned, (const char*)img.data, img.cols, img.rows, path)) Exit("stasm_search_pinned failed: %s", stasm_lasterr()); PrintLandmarks(landmarks, "Final with pinned init"); outimg = img.clone(); DrawLandmarks(outimg, landmarks); imwrite("test_stasm_lib_pinned.bmp", outimg); // test stasm_convert_shape,找到了位置之后的处理 float newlandmarks[2 * stasm_NLANDMARKS]; // x,y coords #if 0 memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float)); stasm_convert_shape(newlandmarks, 68); PrintLandmarks(newlandmarks, "stasm77 to xm2vts"); #endif #if 0 outimg = img.clone(); DrawLandmarks(outimg, newlandmarks, 68); imwrite("test_stasm_lib_68.bmp", outimg); #endif #if 0 memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float)); stasm_convert_shape(newlandmarks, 76); PrintLandmarks(newlandmarks, "stasm77 to stasm76"); #endif #if 0 outimg = img.clone(); DrawLandmarks(outimg, newlandmarks, 76); imwrite("test_stasm_lib_76.bmp", outimg); #endif #if 1 memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float)); stasm_convert_shape(newlandmarks, 22); PrintLandmarks(newlandmarks, "stasm77 to stasm22"); outimg = img.clone(); // DrawLandmarks(outimg, newlandmarks, 22); BiaoDing(outimg,newlandmarks,22); imwrite("test_stasm_lib_22.bmp_biaoding.bmp", outimg); memcpy(newlandmarks, landmarks, 2 * stasm_NLANDMARKS * sizeof(float)); stasm_convert_shape(newlandmarks, 20); PrintLandmarks(newlandmarks, "stasm77 to stasm20"); outimg = img.clone(); //DrawLandmarks(outimg, newlandmarks, 20); BiaoDing(outimg,newlandmarks,19); imwrite("test_stasm_lib_20_biaoding.bmp", outimg); #endif } return 0; // success }
七、运行效果
1)20个landmarks位置,分别为点图和连线图:
2)68个landmarks特征点
3)76个特征点
4)77个特征点(默认)
学习讨论之用,联系作者删除
相关文章推荐
- 完美cvCreateVideoWriter写出AVI文件为空的问题
- 第十五周程序-阅读程序-5
- 【leetcode】171. Excel Sheet Column Number
- 1688 求逆序对
- 【转】Android中的颜色设置
- 算分与数据结构 - 冒泡思想
- 【DAY15】Java第十五天I/O学习笔记
- 在QT的界面工程中添加一个简单的线程socket通信
- 栈帧
- 事件分发拦截机制
- Android 使用xml序列化器生成xml文件
- 提高zxing生成二维码的容错率及zxing生成二维码的边框设置
- Spring AOP 实现原理与 CGLIB 应用
- 提高工作效率
- virtualenv开发环境配置
- 好吧,这又是一个圆形进度条!
- 部门沟通规范
- Express Hello
- 基于Spring的包含特定注解bean的package扫描工具
- Json转换类库