Search code examples
c++opencvclassificationface-recognitiondlib

How to identify stranger in dlib`s one vs one classifier


I`m using a one_vs_one_trainer and one_vs_one_decision_function for classify 128D face descriptors, and i want to detect unknown face.

I`m detecting faces using OpenCV and my wrapper, then i followed the guide and computed the 128D face descriptors, that i stored in files. Next, i trained one_vs_one classifier following this tutorial. All works perfectly, but when i try to classify unknown face it returns some label.

I used code from guides, but if you want to look at my code - it is here

Is there a better way to identify faces? Maybe, its simpler to use OpenCV`s methods, or other from Dlib?


Solution

  • Thanks for Davis!
    Here is forum thread on SourceForge.
    The answer is:

    Use a bunch of binary classifiers rather than one vs one. If all the binary classifiers say they don't match then you know the person doesn't match any of them.

    And i implemented this as follows:

    #include <iostream>
    #include <ctime>
    #include <vector>
    
    #include <dlib/svm.h>
    
    using namespace std;
    using namespace dlib;
    
    int main() {
        typedef matrix<double, 128, 1> sample_type;
    
        typedef histogram_intersection_kernel<sample_type> kernel_type;
    
        typedef svm_c_trainer<kernel_type> trainer_type;
    
        typedef decision_function<kernel_type> classifier_type;
    
        std::vector<sample_type> samples;
        std::vector<double> labels;
    
        sample_type sample;
    
        // Samples ->
    
        sample = -0.104075,0.0353173,...,0.114782,-0.0360935;
        samples.emplace_back(sample);
        labels.emplace_back(0);
        sample = -0.0842,-0.0103397,...,0.0938285,0.010045;
        samples.emplace_back(sample);
        labels.emplace_back(0);
        sample = -0.0978358,0.0709425,...,0.052436,-0.0582029;
        samples.emplace_back(sample);
        labels.emplace_back(0);
        sample = -0.126522,0.0319873,...,0.12045,-0.0277105;
        samples.emplace_back(sample);
        labels.emplace_back(0);
        sample = -0.10335,-0.0261625,...,0.0600661,0.00703168,-8.67462e-05,-0.0598214,-0.104442,-0.046698,0.0553857,-0.0880691,0.0482511,0.0331484;
        samples.emplace_back(sample);
        labels.emplace_back(0);
    
        sample = -0.0747794,0.0599716,...,-0.0440207,-6.45183e-05;
        samples.emplace_back(sample);
        labels.emplace_back(1);
        sample = -0.0280804,0.0900723,...,-0.0267513,0.00824318;
        samples.emplace_back(sample);
        labels.emplace_back(1);
        sample = -0.0721213,0.00700722,...,-0.0128318,0.100784;
        samples.emplace_back(sample);
        labels.emplace_back(1);
        sample = -0.122747,0.0737782,0.0375799,...,0.0168201,-0.0246723;
        samples.emplace_back(sample);
        labels.emplace_back(1);
        sample = -0.0218071,0.118063,...,-0.0735178,0.04046;
        samples.emplace_back(sample);
        labels.emplace_back(1);
    
        sample = -0.0680787,0.0490121,-0.0228516,...,-0.0366242,0.0287891;
        samples.emplace_back(sample);
        labels.emplace_back(2);
        sample = 0.00152394,0.107174,...,-0.0479925,0.0182667;
        samples.emplace_back(sample);
        labels.emplace_back(2);
        sample = -0.0334521,0.165314,...,-0.0385227,-0.0215499;
        samples.emplace_back(sample);
        labels.emplace_back(2);
        sample = 0.0276394,0.106774,...,-0.0496831,-0.020857;
        samples.emplace_back(sample);
        labels.emplace_back(2);
    
        // <- Samples
    
        // Unique labels ->
    
        std::vector<double> total_labels;
        for(double &label : labels) {
            if(find(total_labels.begin(), total_labels.end(), label) == total_labels.end())
                total_labels.emplace_back(label);
        }
    
        // <- Unique labels
    
        // Init trainers ->
    
        std::vector<trainer_type> trainers;
        int num_trainers = total_labels.size() * (total_labels.size() - 1) / 2;
        cout << "Number of trainers is " << num_trainers << endl;
    
        for(int i = 0; i < num_trainers; i++) {
            trainers.emplace_back(trainer_type());
            trainers[i].set_kernel(kernel_type());
            trainers[i].set_c(10);
        }
    
        // <- Init trainers
    
        // Init classifiers ->
    
        std::vector<pair<double, double>> classifiersLabels;
        std::vector<classifier_type> classifiers;
        int label1 = 0, label2 = 1;
        for(trainer_type &trainer : trainers) {
            std::vector<sample_type> samples4pair;
            std::vector<double> labels4pair;
    
            for(int i = 0; i < samples.size(); i++) {
                if(labels[i] == total_labels[label1]) {
                    samples4pair.emplace_back(samples[i]);
                    labels4pair.emplace_back(-1);
                }
                if(labels[i] == total_labels[label2]) {
                    samples4pair.emplace_back(samples[i]);
                    labels4pair.emplace_back(+1);
                }
            }
    
            classifiers.emplace_back(trainer.train(samples4pair, labels4pair));
            classifiersLabels.emplace_back(make_pair(total_labels[label1],
                                                     total_labels[label2]));
    
            label2++;
            if(label2 == total_labels.size()) {
                label1++;
                label2 = label1 + 1;
            }
        }
    
        // <- Init classifiers
    
        double threshold = 0.3;
        auto classify = [&](){
            std::map<double, int> votes;
            for(int i = 0; i < classifiers.size(); i++) {
                cout << "Classifier #" << i << ":" << endl;
    
                double prediction = classifiers[i](sample);
                cout << prediction << ": ";
    
                if(abs(prediction) < threshold) {
                    cout << "-1" << endl;
                } else if (prediction < 0) {
                    votes[classifiersLabels[i].first]++;
                    cout << classifiersLabels[i].first << endl;
                } else {
                    votes[classifiersLabels[i].second]++;
                    cout << classifiersLabels[i].second << endl;
                }
            }
    
            cout << "Votes: " << endl;
            for(auto &vote : votes) {
                cout << vote.first << ": " << vote.second << endl;
            }
    
            auto max = std::max_element(votes.begin(), votes.end(),
                                      [](const pair<double, int>& p1, const pair<double, int>& p2) {
                                          return p1.second < p2.second; });
    
            double label = votes.empty() ? -1 : max->first;
            cout << "Label is " << label << endl;
        };
    
        // Test ->
    
        cout << endl;
    
        sample = -0.0971093, ..., 0.123482, -0.0399552;
        cout << "True: 0 - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0548414, ..., 0.0277335, 0.0460183;
        cout << "True: 1 - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0456186,0.0617834,...,-0.0387607,0.0366309;
        cout << "True: 1 - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0500396, 0.0947202, ..., -0.0540899, 0.0206803;
        cout << "True: 2 - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0702862, 0.065316, ..., -0.0279446, 0.0453012;
        cout << "Unknown - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0789684, 0.0632067, ..., 0.0330486, 0.0117508;
        cout << "Unknown - " << endl;
        classify();
    
        cout << endl;
    
        sample = -0.0941284, 0.0542927, ..., 0.00855513, 0.00840678;
        cout << "Unknown - " << endl;
        classify();
    
        // <- Test
    
        return 0;
    }