2023年11月30日木曜日

顔認証をつくってみた。

 おじさんこの前、顔認証でゲートを通過するところに行きました。

最近だんだん増えてきたよね。

だけれど、きちんと登録したのに何故か通れない。

ふざけやがって・・・・。


なので今回は顔認証システムの作成に挑戦します!


Pythonで手軽に顔認識をやってみる(face-recognition)

https://blog.grasys.io/post/uema/face-recognition/


なるほど、dlibにはすでに顔認証ライブラリがあるのね。

99.38%の正解率らしい。


dlib/examples/dnn_face_recognition_ex.cpp

https://github.com/davisking/dlib/blob/master/examples/dnn_face_recognition_ex.cpp


ここに顔認証のサンプルがあります。
これをちょっと改造するだけじゃん!
顔認証は顔を検出して、次元縮小して、比べたい画像の特徴ベクトルの引き算して距離を求めるだけ。
なんだ引き算するだけじゃん。

dlib/examples/dnn_metric_learning_on_images_ex.cpp

学習のソースコードもとっても簡単だし。顔データもすべてそろっている。
ネットはテンプレートで組む時代なのね。
300万人の顔を学習させたのね。
なんかそこら辺の日本の企業を超えている気がする。
しかもこれ、resnetでmetric learningしているので、顔だけでなく植物の名前など画像ならなんでも学習させることが可能です。曲がったキュウリとかの異常検知もできそうですね。
凄すぎる。

志村けんやチョコプラのIKKOをきちんと認証できるかやってみました。

--------------------------------
#define DLIB_JPEG_SUPPORT 1


#include <dlib_dnn.h>
#include <dlib_gui_widgets.h>
#include <dlib_clustering.h>
#include <dlib_string.h>
#include <dlib_image_io.h>
#include <dlib_image_processing_frontal_face_detector.h>

using namespace dlib;
using namespace std;


template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual = add_prev1<block<N,BN,1,tag1<SUBNET>>>;

template <template <int,template<typename>class,int,typename> class block, int N, template<typename>class BN, typename SUBNET>
using residual_down = add_prev2<avg_pool<2,2,2,2,skip1<tag2<block<N,BN,2,tag1<SUBNET>>>>>>;

template <int N, template <typename> class BN, int stride, typename SUBNET> 
using block  = BN<con<N,3,3,1,1,relu<BN<con<N,3,3,stride,stride,SUBNET>>>>>;

template <int N, typename SUBNET> using ares      = relu<residual<block,N,affine,SUBNET>>;
template <int N, typename SUBNET> using ares_down = relu<residual_down<block,N,affine,SUBNET>>;

template <typename SUBNET> using alevel0 = ares_down<256,SUBNET>;
template <typename SUBNET> using alevel1 = ares<256,ares<256,ares_down<256,SUBNET>>>;
template <typename SUBNET> using alevel2 = ares<128,ares<128,ares_down<128,SUBNET>>>;
template <typename SUBNET> using alevel3 = ares<64,ares<64,ares<64,ares_down<64,SUBNET>>>>;
template <typename SUBNET> using alevel4 = ares<32,ares<32,ares<32,SUBNET>>>;

using anet_type = loss_metric<fc_no_bias<128,avg_pool_everything<
                            alevel0<
                            alevel1<
                            alevel2<
                            alevel3<
                            alevel4<
                            max_pool<3,3,2,2,relu<affine<con<32,7,7,2,2,
                            input_rgb_image_sized<150>
                            >>>>>>>>>>>>;

// ----------------------------------------------------------------------------------------

frontal_face_detector detector;
shape_predictor sp;
anet_type net;


matrix<float, 0, 1> get_face_descriptors(const char* fn,const char* sfn=nullptr)
{
    std::vector<matrix<float, 0, 1>> face_descriptors;
    matrix<rgb_pixel> img;
    load_image(img, fn);
    //image_window win(img);

    std::vector<matrix<rgb_pixel>> faces;
    for (auto face : detector(img))
    {
        auto shape = sp(img, face);
        matrix<rgb_pixel> face_chip;
        extract_image_chip(img, get_face_chip_details(shape, 150, 0.25), face_chip);
        faces.push_back(move(face_chip));
        draw_rectangle(img, face, dlib::rgb_pixel(255,0,0));
        //win.add_overlay(face);
    }

    if (faces.size() == 0)
    {
        return face_descriptors[0];
    }
   face_descriptors = net(faces);

   if (sfn != nullptr) {
       save_bmp(img, sfn);
   }
   //cin.get();
   
   return face_descriptors[0];
}

void save_face_descriptors(const char* fn, const char* sfn,const char* name)
{
    std::vector<matrix<float, 0, 1>> face_descriptors;
    matrix<rgb_pixel> img;
    load_image(img, fn);
    //image_window win(img);

    std::vector<matrix<rgb_pixel>> faces;
    for (auto face : detector(img))
    {
        auto shape = sp(img, face);
        matrix<rgb_pixel> face_chip;
        extract_image_chip(img, get_face_chip_details(shape, 150, 0.25), face_chip);
        faces.push_back(move(face_chip));
        draw_rectangle(img, face, dlib::rgb_pixel(255, 0, 0));
        draw_string(img, dlib::point(face.left(),face.top() + face.height()), name, rgb_pixel(255, 0, 0));
        //win.add_overlay(face);
    }

    if (faces.size() == 0)
    {
        return;
    }
    face_descriptors = net(faces);

    if (sfn != nullptr) {
        save_bmp(img, sfn);
    }
}


struct face_file {
    const char* name;
    const char* fname;
};

std::vector<struct face_file> face_files = {
    {"shimuraken","shimuraken.jpg"},
    {"matsuo","matsuo.jpg"},
    {"ikko","ikko.jpg"},
};

std::vector<const char*> test_files = {
    "shimura_001.jpg",
    "ikko_001.jpg",
    "matsuo_001.jpg",
};

int main(int argc, char** argv)
{

    detector = get_frontal_face_detector();
    deserialize("shape_predictor_5_face_landmarks.dat") >> sp;
    deserialize("dlib_face_recognition_resnet_model_v1.dat") >> net;



    std::vector<matrix<float, 0, 1>> face_descriptors;
    for (int i = 0; i < face_files.size(); i++) {
        face_descriptors.push_back(get_face_descriptors(face_files[i].fname));
    }
    for (int i = 0; i < face_files.size(); i++) {
        matrix<float, 0, 1> face_descriptors2 = get_face_descriptors(test_files[i]);
        printf("------------\n");
        printf("%s\n", test_files[i]);
        //for (int j = 0; j < face_files.size(); j++) {
        //    printf("    length=%f  name=%s  \n", length(face_descriptors[j] - face_descriptors2), face_files[j].name);
        //}
        float len;
        const char* name;
        int pos;
        char outname[256];
        pos = 0;
        len = length(face_descriptors[0] - face_descriptors2);
        name = face_files[0].name;
        for (int j = 1; j < face_files.size(); j++) {
            if (len < length(face_descriptors[j] - face_descriptors2))continue;
            pos = j;
            len = length(face_descriptors[j] - face_descriptors2);
            name = face_files[j].name;
        }
        printf("length=%f  name=%s  \n", len, name);
        sprintf(outname, "out_%s.bmp", name);
        save_face_descriptors(test_files[i], outname,name);
    }

    cout << "hit enter to terminate" << endl;
    cin.get();
}


--------------------------------


結果



文字が小さくて読みにくいですが、仮装しててもきちんと顔認証できるじゃん。
すげー。
顔認証システムって数時間で作れるのね。