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英文鏈接

歡迎志同道合着一起交流!!!
需要源碼的請留言

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章