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

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个特征点(默认)














学习讨论之用,联系作者删除

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值