SeetaFace学习之二

之前的一篇文章,已经给大家介绍了SeetaFace的基础运用--人脸检测,这仅仅只是第一步,我们最关键的地方还是使用这个模型来进行人脸识别,下面就介绍一下这个模型后续的一些模块的功能和运用。

其中第二步应该是进行人脸相似度的比对实验,这个比较好实现,只要之前第一步实现了,这个还是很简单的,有兴趣的同学可以参照这篇文章(SeetaFace教程(二)封装常用功能:提取一幅图像中的人脸特征、计算特征相似性)进行操作

其实这个实验也可以按照知乎作者@何之源的第三篇介绍文档来操作,但不知哪儿飘来的bug,让熟练找bug的我到最后也不知道调试bug是哪儿来的。于是后面就转换了另一个实现。下面介绍一下我在以上作者基础上实验中踩的坑,不感兴趣的可以直接跳过看后面的内容。


一切按照作者的描述配置好以后,我自己也根据依赖再次检查一遍之后,感觉没问题,那就跑咯
第一次踩坑:无法遍历图片文件,明明有文件,却检测到0个对象
解决:直接debug,发现好像文件路径不对,直接放绝对路径!再跑!
第二次踩坑:换绝对路径后运行时抛异常!。。。这个bug思考良久,之间有很多尝试
解决:最后发现是win10下下面代码中while 循环体当中的_findnext(hFile, &fileinfo) == 0函数有毛病,它的第一个参数要改为long long类型的。。。

bool GetFilenameUnderPath(string file_path, std::vector& files) {
    long long   hFile = 0;
    //文件信息  
    struct _finddata_t fileinfo;
    string p;
    if ((hFile = _findfirst(p.assign(file_path).append("\\*").c_str(), &fileinfo)) != -1)
    {
        do
        {
            //如果是目录,迭代之  
            //如果不是,加入列表  
            if ((fileinfo.attrib &  _A_SUBDIR))
            {
                if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                    GetFilenameUnderPath(p.assign(file_path).append("\\").append(fileinfo.name), files);
            }
            else
            {
                char *ext = strrchr(fileinfo.name, '.');
                if (ext) {
                    ext++;
                    if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "png") == 0)
                        files.push_back(p.assign(file_path).append("\\").append(fileinfo.name));
                }
            }
        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }
    return true;
}

第三次踩坑:获取图片以及读取图片特征的时候,抛异常!
未解决:再次debug,发现每次能够读取文件夹当中的第一张图片,第二张就不行了,开始想着是不是没有释放变量,导致堆内对象的冲突,看了看,应该不是,那个函数调用完以后就结束了生命周期,不存在没有回收的情况,而后。。。吃饭去了,懒得找了,找了另外一个项目,功能一样,代码似乎跟这个差不多,没细看,能跑就好了,耐心没了(-


正式内容

参考文章:传送门
之前第一次跑的没问题的话,可以直接按照这后面的介绍来:

总体步骤

新建win32控制台项目,此时需要opencv,我在测试时使用的是opencv2.4.10,其他的应该也行。
① 将项目属性改为x64
② 在配置属性-VC++目录中,将opencv的包含目录和库目录修改好,记得使用x64的lib库
③ 链接器-输入,加入FaceIdentification.lib FaceAlignment.lib FaceDetection.lib opencv_core2410.lib opencv_highgui2410.lib opencv_imgproc2410.lib
④ 与这6个lib文件相对于的dll文件放入可执行文件目录下即可。
⑤ 将seetaface源码中的三个model,放在一起,存于项目文件夹下即可。images文件夹中为一些测试图片

两个头文件:

  • FaceRecognition.h
#ifndef FACERECOGNITION_H  
#define FACERECOGNITION_H  

#include   
#include  
using std::string;
#include   
#include   
#include "face_detection.h"  
#include "face_alignment.h"  
#include "face_identification.h"  

class Detector : public seeta::FaceDetection {
public:
    Detector(const char * model_name);
};

class FaceRecognition {
public:
    FaceRecognition();
    float* NewFeatureBuffer();
    float FeatureCompare(float* feat1, float* feat2);
    int GetFeatureDims();
    bool GetFeature(string filename, float* feat);
    ~FaceRecognition();
public:
    Detector* detector;
    seeta::FaceAlignment* point_detector;
    seeta::FaceIdentification* face_recognizer;

};

#endif // !FACERECOGNITION_H  

  • FaceGroup.h
#ifndef FEATUREGROUP_H  
#define FEATUREGROUP_H  

#include   
#include   
#include   
#include   
#include "FaceRecognition.h"  
struct Feature
{
    std::string filename;
    float* data;
    float similarity_with_goal;
    friend bool operator < (Feature f1, Feature f2)
    {
        return f1.similarity_with_goal < f2.similarity_with_goal;
    }
};

class FeatureGroup
{
public:
    FeatureGroup(int feat_dims, FaceRecognition* fr);
    FeatureGroup(string model_file, FaceRecognition* fr);
    bool AddFeature(float* feat, string filename);
    bool SaveModel(string model_file);
    int GetFeatureDims();
    bool FindTopK(int k, float* feat, std::vector& result);
    ~FeatureGroup();

public:
    std::vector features;

private:
    int feat_dims;
    FaceRecognition* fr;
};



#endif //FEATUREGROUP_H  

三个CPP文件

  • FaceRecognition.cpp
#include "FaceRecognition.h"  

Detector::Detector(const char* model_name) :seeta::FaceDetection(model_name)
{
    this->SetMinFaceSize(40);
    this->SetScoreThresh(2.f);
    this->SetImagePyramidScaleFactor(0.8f);
    this->SetWindowStep(4, 4);
}

FaceRecognition::FaceRecognition()
{
    this->detector = new Detector("model/seeta_fd_frontal_v1.0.bin");
    this->point_detector = new seeta::FaceAlignment("model/seeta_fa_v1.1.bin");
    this->face_recognizer = new seeta::FaceIdentification("model/seeta_fr_v1.0.bin");
}

float* FaceRecognition::NewFeatureBuffer()
{
    return new float[this->face_recognizer->feature_size()];
}

int FaceRecognition::GetFeatureDims()
{
    return this->face_recognizer->feature_size();
}

float FaceRecognition::FeatureCompare(float* feat1, float* feat2)
{
    return this->face_recognizer->CalcSimilarity(feat1, feat2);
}


bool FaceRecognition::GetFeature(string filename, float* feat)
{
    //load image and convert to gray  
    cv::Mat src_img_color = cv::imread(filename, 1);
    cv::Mat src_img_gray;
    cv::cvtColor(src_img_color, src_img_gray, CV_BGR2GRAY);

    //convert to ImageData type  
    seeta::ImageData src_img_data_color(src_img_color.cols, src_img_color.rows, src_img_color.channels());
    src_img_data_color.data = src_img_color.data;

    seeta::ImageData src_img_data_gray(src_img_gray.cols, src_img_gray.rows, src_img_gray.channels());
    src_img_data_gray.data = src_img_gray.data;

    //Detect faces  
    std::vector faces = this->detector->Detect(src_img_data_gray);
    int32_t face_num = static_cast(faces.size());
    if (face_num == 0)
    {
        std::cout << "Faces are not detected." << std::endl;
        return false;
    }

    //Detect 5 facial landmarks  
    seeta::FacialLandmark points[5];
    this->point_detector->PointDetectLandmarks(src_img_data_gray, faces[0], points);

    //Extract face identity feature  
    this->face_recognizer->ExtractFeatureWithCrop(src_img_data_color, points, feat);

    return true;

}

FaceRecognition::~FaceRecognition()
{
    if (detector)
        delete detector;
    if (point_detector)
        delete point_detector;
    if (face_recognizer)
        delete face_recognizer;
}
  • FeatureGroup.cpp
#include "FeatureGroup.h"  

FeatureGroup::FeatureGroup(int feat_dims, FaceRecognition* fr)
{
    this->feat_dims = feat_dims;
    this->fr = fr;
}

FeatureGroup::FeatureGroup(string model_file, FaceRecognition* fr)
{
    std::ifstream file;
    file.open(model_file);
    int size;
    float* new_feat;
    char* buffer = new char[1000];
    //从模型读取数据  
    file >> size;
    file >> this->feat_dims;
    for (int i = 0; i < size; i++)
    {
        Feature tmp;
        file.getline(buffer, 1000);
        while (buffer[0] == '\0' || buffer[0] == ' ')
        {
            file.getline(buffer, 1000);
        }
        tmp.filename = buffer;
        new_feat = new float[this->feat_dims];
        for (int j = 0; j < this->feat_dims; j++)
        {
            file >> new_feat[j];
        }
        tmp.data = new_feat;
        this->features.push_back(tmp);
    }
    file.close();
    this->fr = fr;
}

int FeatureGroup::GetFeatureDims()
{
    return this->feat_dims;
}

bool FeatureGroup::AddFeature(float* feat, string filename)
{
    Feature tmp;
    float* new_feat = new float[this->feat_dims];
    //memcpy(new_feat, feat, sizeof(new_feat) * this->feat_dims);// 这句话执行有问题,改成下面的循环  
    for (int i = 0; i < this->feat_dims; i++)
    {
        new_feat[i] = feat[i];
    }
    tmp.data = new_feat;
    tmp.filename = filename;
    this->features.push_back(tmp);
    return true;
}

bool FeatureGroup::SaveModel(string model_file)
{
    std::ofstream file;
    file.open(model_file);
    file << int(this->features.size()) << std::endl; //先输出特征的个数  
    file << this->feat_dims << std::endl; //再输出特征的维数  
                                          //依次写入数据  
    for (int i = 0; i < int(this->features.size()); i++)
    {
        file << this->features[i].filename << std::endl;
        for (int j = 0; j < this->feat_dims; j++)
        {
            file << this->features[i].data[j] << " ";
        }
        file << std::endl;
    }
    file.close();
    return true;
}

bool FeatureGroup::FindTopK(int k, float* feat, std::vector& result)
{
    std::cout << "Calculating Similarities..." << std::endl;
    for (int i = 0; i < int(this->features.size()); i++)
    {
        this->features[i].similarity_with_goal = this->fr->FeatureCompare(this->features[i].data, feat);
    }
    std::cout << "Finding Topk..." << std::endl;
    std::priority_queue q;
    for (int i = 0; i < int(this->features.size()); i++)
    {
        q.push(this->features[i]);
    }
    for (int i = 0; i < k; i++)
    {
        if (q.empty())
            return true;
        result.push_back(q.top());
        q.pop();
    }

    return true;

}

FeatureGroup::~FeatureGroup()  //如果出错,再回来修改  
{
    for (int i = 0; i < int(this->features.size()); i++)
    {
        delete[](this->features[i].data);
    }

}
  • main.cpp
#include "FaceRecognition.h"  
#include "FeatureGroup.h"  

/*
* given two images and calculate the similarity
*/
int faceverification()
{
    FaceRecognition fr;
    float* feat1 = fr.NewFeatureBuffer();
    float* feat2 = fr.NewFeatureBuffer();

    std::vector> pic_pair;
    pic_pair.push_back(std::make_pair("0_1.jpg", "0_2.jpg"));
    pic_pair.push_back(std::make_pair("1_1.jpg", "1_2.jpg"));
    pic_pair.push_back(std::make_pair("2_1.jpg", "2_2.jpg"));

    double averageDetectTime = 0.0;
    for (size_t i = 0; i < pic_pair.size(); i++)
    {
        long t0 = cv::getTickCount();
        fr.GetFeature(pic_pair[i].first, feat1);
        fr.GetFeature(pic_pair[i].second, feat2);
        float sim = fr.FeatureCompare(feat1, feat2);
        std::cout << "similarity:" << sim << std::endl;

        long t1 = cv::getTickCount();
        double secs = (t1 - t0) / cv::getTickFrequency();
        averageDetectTime += secs;
        std::cout << "Image pair " << i << " takes " << secs << " seconds to detect,alignment and verification !" << std::endl;;

    }
    std::cout << std::endl << "All verification takes " << averageDetectTime << " secs!" << std::endl;
    std::cout << "The average verification times for one image takes " << double(averageDetectTime / pic_pair.size() / 2) << " secs !" << std::endl;

    delete feat1;
    delete feat2;
    return 0;
}


bool GetAllFileNames(string file_path, std::vector& files) {
    intptr_t hFile = 0;
    //文件信息    
    struct _finddata_t fileinfo;
    string p;
    if ((hFile = _findfirst(p.assign(file_path).append("\\*").c_str(), &fileinfo)) != -1)
    {
        do
        {
            //如果是目录,迭代之    
            //如果不是,加入列表    
            if ((fileinfo.attrib &  _A_SUBDIR))
            {
                if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                    GetAllFileNames(p.assign(file_path).append("\\").append(fileinfo.name), files);
            }
            else
            {
                char *ext = strrchr(fileinfo.name, '.');
                if (ext) {
                    ext++;
                    if (_stricmp(ext, "jpg") == 0 || _stricmp(ext, "png") == 0)
                        files.push_back(p.assign(file_path).append("\\").append(fileinfo.name));
                }
            }
        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }
    return true;
}


int main(int argc, char* argv[])
{
    int choice;
    std::cout << "Input 1 to build database index, 2 to test image: ";
    std::cin >> choice;
    if (1 == choice)
    {
        std::vector filenames;
        GetAllFileNames("./images", filenames);
        std::cout << "Detected " << filenames.size() << " images..." << std::endl;
        FaceRecognition fr;
        FeatureGroup fg(fr.GetFeatureDims(), &fr);
        float * feat = fr.NewFeatureBuffer();

        double averageExtractFeatureTime = 0.0;
        for (int i = 0; i < filenames.size(); i++)
        {
            long t0 = cv::getTickCount();
            if (fr.GetFeature(filenames[i], feat))
            {
                fg.AddFeature(feat, filenames[i]);
            }
            long t1 = cv::getTickCount();
            double secs = (t1 - t0) / cv::getTickFrequency();
            averageExtractFeatureTime += secs;
            if ((i + 1) % 5 == 0)
            {
                std::cout << i + 1 << " / " << int(filenames.size()) << "  has been extracted!" << std::endl;
            }
        }
        std::cout << std::endl << "All verification takes " << averageExtractFeatureTime << " secs!" << std::endl;
        std::cout << "The average extract feature times for one image takes " << double(averageExtractFeatureTime / filenames.size() / 2) << " secs !" << std::endl;

        fg.SaveModel("feature.index");
        std::cout << "Feature Extraction has been finished!" << std::endl;
    }
    else if (2 == choice)
    {
        FaceRecognition fr;
        string pic_file_path;
        std::cout << "Loading DataBase..." << std::endl;
        FeatureGroup fg("feature.index", &fr);
        std::cout << "Database has been loaded!" << std::endl;
        float * feat = fr.NewFeatureBuffer();
        while (true)
        {
            std::vector result;
            std::cout << "please input your filename:";
            std::cin >> pic_file_path;
            if (!fr.GetFeature(pic_file_path, feat))
            {
                std::cout << "Wrong Filename or Can't detect face..." << std::endl;
                continue;
            }
            fg.FindTopK(10, feat, result);
            for (int i = 0; i < result.size(); i++)
            {
                std::cout << "Top " << i + 1 << " : " << result[i].filename << " Similarity: " << result[i].similarity_with_goal << std::endl;

            }
        }
        std::cout << std::endl;
    }

    return 0;
}

大体文件结构如下

其中有些冗余的东西,但大体如下


structure.PNG

测试

images文件夹下如下图所示,提取七个人的特征,最后做个相似度排行


storage.PNG

测试图片如下:


刘德华.PNG

输出结果:
test.PNG

有人故意拿比较难辩别的图片做了测试,效果还是很好的,具体我没做测试,有兴趣的童鞋可以去探究一下

你可能感兴趣的:(SeetaFace学习之二)