Active Shape Models with Stasm 接口使用

本人只是使用了stasm接口中的一部分功能,直接拿他们训练好的模型进行使用,整个结构精简过,若需要完整的stasm文件。可下在后面链接下载完整版的。

一、简介 Stasm:
1、stasm是一个c++软件包,用来定位人脸中面部的landmarks(特征点)。输入带有人脸图像,返回landmarks的位置。特征点坐标按照[x,y],[x,y]…………顺序存放在一维数组中。
2、Stasm被设计工作在大约垂直(竖直)且带有中性表情的直视的人脸。对于生气或者带有表情的将得到不好的效果。尝尝会出现下巴部位不准,即使是闭上眼睛也会标记出眼球,这个它用的算法思想有关。
3、Stasm采用的HAT(Histogram Array Transform)描述子来做模版匹配,类似与SIFT描述子。

二、测试和运行
用vs2010打开minimal.sln文件,进入工程后,假如opencv的配置环境,包括(.h,lib,和dll),就可以运行看到效果。本人实验室用的是2.4.8版本的。这里可能需要小小的配置一下。

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

1、简单接口:
最简单的方法:使用stasm_search_single函数,该函数使用opencv的前侧人脸检测器找到图像中最大的人脸,并且返回landmarks的位置。
人脸的宽度至少是图像宽度的10%。本人提供的demo中调用的就是这个函数。因为目前我的项目中只涉及到一张图中只有一个人脸的情况。

**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倍的大小,这样可以简单的人脸,但是也降低了检测的速度。
对于左侧人脸,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
};

六、测试程序
该程序直接调用摄像头,实时观察人脸五官的定位。也可以自己修改一下读取图片进行处理。

// minimal.cpp: Display the landmarks of a face in an image.
//              This demonstrates stasm_search_single.

#include <stdio.h>
#include <stdlib.h>
#include "opencv/highgui.h"
#include "opencv2/objdetect/objdetect.hpp"  
#include "opencv2/highgui/highgui.hpp"  
#include "opencv2/imgproc/imgproc.hpp"  
#include <iostream>  
#include <stdio.h>  
#include <opencv2/core/core.hpp>   
#include "stasm_lib.h"
#include <core/core.hpp>
#include <direct.h>  
#include <crtdbg.h>
#include "stasm_landmarks.h"
using namespace cv ;

int main()
{
    VideoCapture capture(0);
    if (!capture.isOpened())
    { 
        printf("打开摄像头失败");
        return -1;
    }//检测是否成功打开摄像头
    Mat img2;
    while (1)
    {
        static const char* const path = "../data/testface.jpg";
        capture>> img2;
        if (!capture.read(img2))//这个判断的必须的
        {
            break;
        }
        Mat gray;
        Mat img;
        cvtColor(img2,img,CV_BGR2GRAY,0);
        if (waitKey(30) >= 0)//刷新间隔30ms,按任意键退出
            break;
        int foundface;
        float landmarks[2 * stasm_NLANDMARKS]={0}; // x,y coords (note the 2)
        if (!stasm_search_single(&foundface, landmarks,(const char*)img.data, img.cols, img.rows, path, "../data"))
        {
            printf("Error in stasm_search_single: %s\n", stasm_lasterr());
            exit(1);
        }
        if (!foundface)
            printf("No face found\n");
        else
        {
            stasm_force_points_into_image(landmarks, img.cols, img.rows);//坐标在范围限制
            for (int i = 0; i <stasm_NLANDMARKS; i++)
            {  
                circle(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),3,CV_RGB(255,255,255),1,8, 0);
            }
#if 0
            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, (const char*)img.data, img.cols, img.rows, path))  
                printf("Nooooooooooooooooooooooooooooooooo");
            for (int i = 0; i <stasm_NLANDMARKS; i++)
            {  
                circle(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),3,CV_RGB(255,0,255),1,8, 0);
            }
#endif
            //  76#if 1
            stasm_convert_shape(landmarks, 76);
            //下巴轮廓[0~15)共15个点;
            int start=0;
            int end=15;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            ////    右眉毛[15~21)共6个点;
            start=15;
            end=21;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
            //  //右眉毛[21~27)共6个点;
            start=21;
            end=27;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
            //左眼球[27~31)共4个点;
                start=27;
                end=31;
                for (int i = start+1; i <end; i++)
                {  
                    line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
                }
                line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
                //右眼球[32~36)共4个点;
                start=32;
                end=36;
                for (int i = start+1; i <end; i++)
                {  
                    line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
                }
                line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
            ////鼻梁[37~46)共9个点
            start=37;
            end=46;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            ////嘴外轮廓[48~60)共12个点
            start=48;
            end=60;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
            ////2[60~66)共6个点
            start=60;
            end=66;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(255,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(255,255,255),1,8, 0);
            //左眼球
            start=68;
            end=72;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(0,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(0,255,255),1,8, 0);
            //右眼球
            start=72;
            end=76;
            for (int i = start+1; i <end; i++)
            {  
                line(img2,Point(cvRound(landmarks[i*2]),cvRound(landmarks[i*2+1])),Point(cvRound(landmarks[i*2-2]),cvRound(landmarks[i*2-1])),CV_RGB(0,255,255),1,8, 0);
            }
            line(img2,Point(cvRound(landmarks[end*2-2]),cvRound(landmarks[end*2-1])),Point(cvRound(landmarks[start*2]),cvRound(landmarks[start*2+1])),CV_RGB(0,255,255),1,8, 0);
#endif
        }
        int thickness = 1;  
        int lineType = 8;  
        cvNamedWindow("show_image",256);
        cv::imshow("show_image", img2);
    }
    return 0;
}

76点图这里写图片描述

vs2010+opencv2+ASM 五官特征点定位源码
Active Shape Models with Stasm英文链接

欢迎志同道合着一起交流!!!
需要源码的请留言

猜你喜欢

转载自blog.csdn.net/liufanghuangdi/article/details/51886100