zoukankan      html  css  js  c++  java
  • 基于OpenCV性别识别

    叙述性说明

    所谓的性别识别推断检测到的面部是男性还是女性。它是一个二值分类问题。

    识别算法可以用于SVM,BP神经网络。LDA,PCA,PCA+LDA等等。OpenCV官网给出的文档是基于Fisherfaces检測器(LDA)方法实现的。链接:http://docs.opencv.org/modules/contrib/doc/facerec/tutorial/facerec_gender_classification.html#id5 。这篇博文(http://www.bytefish.de/blog/gender_classification/)中也是採用OpenCV官网的方法。据称有98%的正确率,我在百度图片找了一些数据測试了下,仅仅有大概50%多的识别率。原因是他的数据集是经过严格标定的。好像是眼睛的是对齐的。

    实际应用中不太可能会遇到这样的情况吧。CSDN还有两篇博客也介绍到这个性格识别(http://blog.csdn.net/kklots/article/details/8247738 http://blog.csdn.net/kklots/article/details/9285505)文章写得非常好,一看就是大牛。博文中也是測试了LDA的方法,正确率也是出奇的低。採用的是PCA+LDA的方法。通过改进能达到接近90%的正确率。

    博文指出PCA+LDA比单纯的LDA和PCA识别率都高,但我对博文中的PCA+LDA程序和官网的PCA程序測试了下,发现PCA的正确率会高那么一两个点。难道又是数据的问题?

    数据

    採集数据一方面能够採用开源的人脸库。还有一方面能够自己去百度图片下载图片。去百度或谷歌图片分别搜索“男明星头像”“女明星头像”的关键字批量下载。这里当然须要批量下载利器。

    然后利用人脸检測器过滤检測出头像,然后归一化检測出来的图像,保存在本地。这样主要的数据集就有了。

    当然我也会附上我採集的数据和工程文件(特此声明,全部图片均来自网络)

    測试程序

    创建CSV文件的python代码:

    import sys
    import os.path
    
    
    if __name__ == "__main__":
    
        if len(sys.argv) != 3:
            print "usage: create_csv <base_path> <SAVE_FILE_NAME>"
            sys.exit(1)
    
        BASE_PATH=sys.argv[1]
        FILE_NAME = sys.argv[2]
        SEPARATOR=";"
        fh = open(FILE_NAME,'w')
    
        label = 0
        for dirname, dirnames, filenames in os.walk(BASE_PATH):
            for subdirname in dirnames:
                subject_path = os.path.join(dirname, subdirname)
                for filename in os.listdir(subject_path):
                    abs_path = "%s/%s" % (subject_path, filename)
                    ##print "%s%s%d" % (abs_path, SEPARATOR, label)
                    ##print "%s  %s" % (dirname, subject_path)
    
                    fh.write(abs_path)
                    fh.write(SEPARATOR)
                    if dirname.find("female") > 0 :
                        label = 1
                    else:
                        label = 0
                    fh.write(str(label))
                    fh.write("
    ")
        fh.close()

    測试性别识别的程序

    // gender.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <math.h>
    int g_howManyPhotoForTraining   =   260;
    //每一个人取出8张作为训练
    int g_photoNumberOfOnePerson    =   279;
    //ORL数据库每一个人10张图像
    using namespace cv;
    using namespace std;
    
    static Mat norm_0_255(InputArray _src) {
        Mat src = _src.getMat();
        // 创建和返回一个归一化后的图像矩阵:
        Mat dst;
        switch(src.channels()) {
    case1:
            cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC1);
            break;
    case3:
            cv::normalize(_src, dst, 0,255, NORM_MINMAX, CV_8UC3);
            break;
        default:
            src.copyTo(dst);
            break;
        }
        return dst;
    }
    //使用CSV文件去读图像和标签,主要使用stringstream和getline方法
    static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator =';') {
        std::ifstream file(filename.c_str(), ifstream::in);
        if (!file) {
            string error_message ="No valid input file was given, please check the given filename.";
            CV_Error(CV_StsBadArg, error_message);
        }
        string line, path, classlabel;
        while (getline(file, line)) {
            stringstream liness(line);
            getline(liness, path, separator);
            getline(liness, classlabel);
            if(!path.empty()&&!classlabel.empty()) {
                images.push_back(imread(path, 0));
                labels.push_back(atoi(classlabel.c_str()));
            }
        }
    }
    
    void train_and_test_lda()
    {
        string fn_csv = string("at.txt");
        //string fn_csv = string("feret.txt");
        vector<Mat> allImages,train_images,test_images;
        vector<int> allLabels,train_labels,test_labels;
        try {
            read_csv(fn_csv, allImages, allLabels);
        } catch (cv::Exception& e) {
            cerr <<"Error opening file "<< fn_csv <<". Reason: "<< e.msg << endl;
            // 文件有问题,我们啥也做不了了。退出了
            exit(1);
        }
        if(allImages.size()<=1) {
            string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
            CV_Error(CV_StsError, error_message);
        }
    
        for(int i=0 ; i<allImages.size() ; i++)
            equalizeHist(allImages[i],allImages[i]);
    
        int photoNumber = allImages.size();
        for(int i=0 ; i<photoNumber ; i++)
        {
            if((i%g_photoNumberOfOnePerson)<g_howManyPhotoForTraining)
            {
                train_images.push_back(allImages[i]);
                train_labels.push_back(allLabels[i]);
            }
            else
            {
                test_images.push_back(allImages[i]);
                test_labels.push_back(allLabels[i]);
            }
        }
    
        /*Ptr<FaceRecognizer> model = createEigenFaceRecognizer();//定义pca模型  
        model->train(train_images, train_labels);//训练pca模型。这里的model包括了全部特征值和特征向量。没有损失  
        model->save("eigenface.yml");//保存训练结果。供检測时使用  */
    
        Ptr<FaceRecognizer> fishermodel = createFisherFaceRecognizer();  
        fishermodel->train(train_images,train_labels);//用保存的降维后的图片来训练fishermodel,后面的内容与原始代码就没什么变化了  
        fishermodel->save("fisherlda.yml");
        int iCorrectPrediction = 0;
        int predictedLabel;
        int testPhotoNumber = test_images.size();
        for(int i=0;i<testPhotoNumber;i++)
        {
            predictedLabel = fishermodel->predict(test_images[i]);
            if(predictedLabel == test_labels[i])
                iCorrectPrediction++;
        }
        string result_message = format("Test Number = %d / Actual Number = %d.", testPhotoNumber, iCorrectPrediction);
        cout << result_message << endl;
        cout<<"accuracy = "<<float(iCorrectPrediction)/testPhotoNumber<<endl;
    }
    
    void train_and_test_pca()
    {
        string fn_csv = string("at.txt");
        //string fn_csv = string("feret.txt");
        vector<Mat> allImages,train_images,test_images;
        vector<int> allLabels,train_labels,test_labels;
        try {
            read_csv(fn_csv, allImages, allLabels);
        } catch (cv::Exception& e) {
            cerr <<"Error opening file "<< fn_csv <<". Reason: "<< e.msg << endl;
            // 文件有问题。我们啥也做不了了,退出了
            exit(1);
        }
        if(allImages.size()<=1) {
            string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
            CV_Error(CV_StsError, error_message);
        }
    
        for(int i=0 ; i<allImages.size() ; i++)
            equalizeHist(allImages[i],allImages[i]);
    
        int photoNumber = allImages.size();
        for(int i=0 ; i<photoNumber ; i++)
        {
            if((i%g_photoNumberOfOnePerson)<g_howManyPhotoForTraining)
            {
                train_images.push_back(allImages[i]);
                train_labels.push_back(allLabels[i]);
            }
            else
            {
                test_images.push_back(allImages[i]);
                test_labels.push_back(allLabels[i]);
            }
        }
    
        Ptr<FaceRecognizer> model = createEigenFaceRecognizer();//定义pca模型  
        model->train(train_images, train_labels);//训练pca模型,这里的model包括了全部特征值和特征向量。没有损失  
        model->save("eigenfacepca.yml");//保存训练结果,供检測时使用  
        int iCorrectPrediction = 0;
        int predictedLabel;
        int testPhotoNumber = test_images.size();
        for(int i=0;i<testPhotoNumber;i++)
        {
            predictedLabel = model->predict(test_images[i]);
            if(predictedLabel == test_labels[i])
                iCorrectPrediction++;
        }
        string result_message = format("Test Number = %d / Actual Number = %d.", testPhotoNumber, iCorrectPrediction);
        cout << result_message << endl;
        cout<<"accuracy = "<<float(iCorrectPrediction)/testPhotoNumber<<endl;
    }
    
    void train_and_test()
    {
        string fn_csv = string("at.txt");
        //string fn_csv = string("feret.txt");
        vector<Mat> allImages,train_images,test_images;
        vector<int> allLabels,train_labels,test_labels;
        try {
            read_csv(fn_csv, allImages, allLabels);
        } catch (cv::Exception& e) {
            cerr <<"Error opening file "<< fn_csv <<". Reason: "<< e.msg << endl;
            // 文件有问题,我们啥也做不了了。退出了
            exit(1);
        }
        if(allImages.size()<=1) {
            string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!";
            CV_Error(CV_StsError, error_message);
        }
    
        for(int i=0 ; i<allImages.size() ; i++)
            equalizeHist(allImages[i],allImages[i]);
    
        int photoNumber = allImages.size();
        for(int i=0 ; i<photoNumber ; i++)
        {
            if((i%g_photoNumberOfOnePerson)<g_howManyPhotoForTraining)
            {
                train_images.push_back(allImages[i]);
                train_labels.push_back(allLabels[i]);
            }
            else
            {
                test_images.push_back(allImages[i]);
                test_labels.push_back(allLabels[i]);
            }
        }
    
        Ptr<FaceRecognizer> model = createEigenFaceRecognizer();//定义pca模型  
        model->train(train_images, train_labels);//训练pca模型。这里的model包括了全部特征值和特征向量,没有损失  
        model->save("eigenface.yml");//保存训练结果。供检測时使用  
        Mat eigenvalues = model->getMat("eigenvalues");//提取model中的特征值。该特征值默认由大到小排列  
        Mat W = model->getMat("eigenvectors");//提取model中的特征向量,特征向量的排列方式与特征值排列顺序一一相应  
        int xth = 121;//打算保留前121个特征向量,代码中没有体现原因。但选择121是经过斟酌的,首先,在我的实验中。"前121个特征值之和/全部特征值总和>0.97"。其次,121=11^2,能够将结果表示成一个11*11的2维图像方阵,交给fisherface去计算。

    vector<Mat> reduceDemensionimages;//降维后的图像矩阵 vector<Mat> testreduceDemensionimages; Mat evs = Mat(W, Range::all(), Range(0, xth));//选择前xth个特征向量,其余舍弃 Mat mean = model->getMat("mean"); for(int i=0;i<train_images.size();i++) { Mat projection = subspaceProject(evs, mean, train_images[i].reshape(1,1));//做子空间投影 reduceDemensionimages.push_back(projection.reshape(1,sqrt(xth*1.0)));//将获得的子空间系数表示映射成2维图像,并保存起来 } for(int i=0;i<test_images.size();i++) { Mat projection = subspaceProject(evs, mean, test_images[i].reshape(1,1));//做子空间投影 testreduceDemensionimages.push_back(projection.reshape(1,sqrt(xth*1.0)));//将获得的子空间系数表示映射成2维图像。并保存起来 } Ptr<FaceRecognizer> fishermodel = createFisherFaceRecognizer(); fishermodel->train(reduceDemensionimages,train_labels);//用保存的降维后的图片来训练fishermodel。后面的内容与原始代码就没什么变化了 fishermodel->save("fisher.yml"); int iCorrectPrediction = 0; int predictedLabel; int testPhotoNumber = test_images.size(); for(int i=0;i<testPhotoNumber;i++) { predictedLabel = fishermodel->predict(testreduceDemensionimages[i]); if(predictedLabel == test_labels[i]) iCorrectPrediction++; } string result_message = format("Test Number = %d / Actual Number = %d.", testPhotoNumber, iCorrectPrediction); cout << result_message << endl; cout<<"accuracy = "<<float(iCorrectPrediction)/testPhotoNumber<<endl; } void test_pca() { string fn_csv = string("test.txt"); vector<Mat> allImages; vector<int> allLabels; try { read_csv(fn_csv, allImages, allLabels); } catch (cv::Exception& e) { cerr <<"Error opening file "<< fn_csv <<". Reason: "<< e.msg << endl; // 文件有问题,我们啥也做不了了,退出了 exit(1); } if(allImages.size()<=1) { string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!"; CV_Error(CV_StsError, error_message); } Ptr<FaceRecognizer> model = createEigenFaceRecognizer();//定义pca模型 model->load("eigenfacepca.yml");//保存训练结果。供检測时使用 int iCorrectPrediction = 0; int predictedLabel; int testPhotoNumber = allImages.size(); for(int i=0;i<testPhotoNumber;i++) { predictedLabel = model->predict(allImages[i]); if(predictedLabel == allLabels[i]) iCorrectPrediction++; } string result_message = format("Test Number = %d / Actual Number = %d.", testPhotoNumber, iCorrectPrediction); cout << result_message << endl; cout<<"accuracy = "<<float(iCorrectPrediction)/testPhotoNumber<<endl; } void test() { string fn_csv = string("test.txt"); vector<Mat> allImages; vector<int> allLabels; try { read_csv(fn_csv, allImages, allLabels); } catch (cv::Exception& e) { cerr <<"Error opening file "<< fn_csv <<". Reason: "<< e.msg << endl; // 文件有问题。我们啥也做不了了,退出了 exit(1); } if(allImages.size()<=1) { string error_message ="This demo needs at least 2 images to work. Please add more images to your data set!"; CV_Error(CV_StsError, error_message); } Ptr<FaceRecognizer> model = createEigenFaceRecognizer();//定义pca模型 model->load("eigenface.yml");//保存训练结果。供检測时使用 Mat eigenvalues = model->getMat("eigenvalues");//提取model中的特征值,该特征值默认由大到小排列 Mat W = model->getMat("eigenvectors");//提取model中的特征向量,特征向量的排列方式与特征值排列顺序一一相应 int xth = 121;//打算保留前121个特征向量,代码中没有体现原因,但选择121是经过斟酌的,首先,在我的实验中。"前121个特征值之和/全部特征值总和>0.97";其次,121=11^2,能够将结果表示成一个11*11的2维图像方阵,交给fisherface去计算。 vector<Mat> reduceDemensionimages;//降维后的图像矩阵 Mat evs = Mat(W, Range::all(), Range(0, xth));//选择前xth个特征向量。其余舍弃 Mat mean = model->getMat("mean"); for(int i=0;i<allImages.size();i++) { Mat projection = subspaceProject(evs, mean, allImages[i].reshape(1,1));//做子空间投影 reduceDemensionimages.push_back(projection.reshape(1,sqrt(xth*1.0)));//将获得的子空间系数表示映射成2维图像,并保存起来 } Ptr<FaceRecognizer> fishermodel = createFisherFaceRecognizer(); fishermodel->load("fisher.yml"); int iCorrectPrediction = 0; int predictedLabel; int testPhotoNumber = allImages.size(); for(int i=0;i<testPhotoNumber;i++) { predictedLabel = fishermodel->predict(reduceDemensionimages[i]); if(predictedLabel == allLabels[i]) iCorrectPrediction++; } string result_message = format("Test Number = %d / Actual Number = %d.", testPhotoNumber, iCorrectPrediction); cout << result_message << endl; cout<<"accuracy = "<<float(iCorrectPrediction)/testPhotoNumber<<endl; } int main() { cout<<"lda = "<<endl; train_and_test_lda(); cout<<"pca = "<<endl; train_and_test_pca(); cout<<"pca+lda = "<<endl; train_and_test(); /*test(); test_pca();*/ return 0 ; }

    整个工程文件和数据下载链接 http://download.csdn.net/detail/zwhlxl/8510649

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    CCPC2018-湖南全国邀请赛
    2019中山大学程序设计竞赛
    [POJ]poj1185 炮兵营地(状压DP)
    [CF]Codeforces Round #551 (Div. 2)
    [EOJ]2019 ECNU XCPC April Selection #1
    [现场赛]“新智认知”杯上海高校程序设计竞赛暨第十七届上海大学程序设计春季联赛
    欧拉函数 欧拉筛法 欧拉定理
    [CF]301D Yaroslav and Divisors
    岸芷汀兰的诗集(持续更新)
    模板柱(持续更新)
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4795271.html
Copyright © 2011-2022 走看看